beaker 4.23.2 → 4.27.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7a6dab8f2bbd3711c1807aa31ef4947cfffb0c80cfe291dfc3261a677c9d618
4
- data.tar.gz: 16dc6decc29e71095f4525b7aa3f301bda493a4cf124695bebeefa6cb695f3fa
3
+ metadata.gz: f1708680196db2be9b63b514162605faa19df47fb2b8f13bfd336af02b464118
4
+ data.tar.gz: 8ea244f5c438736aa003db09aa4d6f8bd7b96f3f99988c5bad0f6302a88f598c
5
5
  SHA512:
6
- metadata.gz: 1ad64e24fd5672ff23b86fc601b7a50d0b1f7b048fde2c8f2af5eb45699ab22bbce0dac53ea1dc8ab4bd887279079a6689f74a1748e03206234f100f5ce08b88
7
- data.tar.gz: 5d32208fd3bff7fe6cc06482ccbb7407318a54dafc3141765e32baf726913c9f7b9af4421e30217642fa993cbc1bdef239afe530b35cb3986017a5bef346f696
6
+ metadata.gz: f3811e71f7e592d024150b6470610df72dbc466cc8bdfeea50e9eb4cd57227892d6bd005e071a710516992a4dc94e5a2c0009970674a8bc0ffd998f494ada76f
7
+ data.tar.gz: cce923c69673ab3e5aea1a7022a2b94ee9e5205b700885cf389b5734f71b60d78c617d4b44214ea8a3fc93614ff1be3f4d580d61e8381afe03d01dcb462d8c15
@@ -0,0 +1,8 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "13:00"
8
+ open-pull-requests-limit: 10
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
3
  --tty
4
+ --fail-fast
@@ -1,5 +1,5 @@
1
1
  before_install:
2
- - gem update --system 2.2.1
2
+ - gem update
3
3
  - gem --version
4
4
  language: ruby
5
5
  script: "bundle exec rake travis"
@@ -7,6 +7,4 @@ notifications:
7
7
  email: false
8
8
  rvm:
9
9
  - 2.6
10
- - 2.0.0
11
- - 1.9.3
12
- - 1.8.7-p374
10
+ - 2.4
@@ -20,13 +20,67 @@ The headers used in [Keep a Changelog](http://keepachangelog.com) are:
20
20
  - Fixed - for any bug fixes.
21
21
  - Security - in case of vulnerabilities.
22
22
 
23
- # [Unreleased](https://github.com/puppetlabs/beaker/compare/4.23.2...master)
23
+ # [Unreleased](https://github.com/puppetlabs/beaker/compare/4.27.0...master)
24
+
25
+ # [4.27.1](https://github.com/puppetlabs/beaker/compare/4.27.0...4.27.1) - 09-29-2020
26
+
27
+ ### Changed
28
+
29
+ - Update net-scp requirement from "~> 1.2" to ">= 1.2, < 4.0"
30
+
31
+ ### Fixed
32
+
33
+ - Handle systems going back in time after reboot
34
+ - Enhanced error handling during the reboot sequence
35
+ - Fixed time check logic during reboot
36
+ - Wrap paths around "" on pswindows
37
+
38
+ # [4.27.0](https://github.com/puppetlabs/beaker/compare/4.26.0...4.27.0) - 07-24-2020
39
+
40
+ ### Changed
41
+
42
+ - Updated dependency versions and minimum Ruby version in gemspec to Ruby 2.4, which is the minimum
43
+ version Beaker will run with.
44
+ - Added Travis unit testing and disabled Jenkins integrations in preparation for transferring the
45
+ repo to Vox Pupuli
46
+
47
+
48
+ # [4.26.0](https://github.com/puppetlabs/beaker/compare/4.25.0...4.26.0)
49
+
50
+ ### Changed
51
+
52
+ - Fixed deprecated SSH option handling for `verify_ssh_key` being passed into Net::SSH. #1655
53
+
54
+ ### Removed
55
+
56
+ - Removed deprecated use of `paranoid` flag with Net::SSH. #1655
57
+
58
+ # [4.25.0](https://github.com/puppetlabs/beaker/compare/4.24.0...4.25.0)
59
+
60
+ ### Added
61
+
62
+ - Execution of Beaker directly through ruby on localhost #1637 ([#1637](https://github.com/puppetlabs/beaker/pull/1637))
63
+
64
+ ### Fixed
65
+
66
+ - Reliability improvements to the `Host#reboot` method ([#1656](https://github.com/puppetlabs/beaker/pull/1656)) ([#1659](https://github.com/puppetlabs/beaker/pull/1659))
67
+
68
+ # [4.24.0](https://github.com/puppetlabs/beaker/compare/4.23.0...4.24.0) - 2020-06-05
69
+
70
+ ### Added
71
+
72
+ - Host method which ([#1651](https://github.com/puppetlabs/beaker/pull/1651))
73
+
74
+ ### Fixed
75
+
76
+ - Fixed implementation for cat and file_exists? host methods for PSWindows ([#1654](https://github.com/puppetlabs/beaker/pull/1654))
77
+ - Fixed implementation for mkdir_p host method for PSWindows ([#1657](https://github.com/puppetlabs/beaker/pull/1657))
24
78
 
25
79
  # [4.23.2](https://github.com/puppetlabs/beaker/compare/4.23.1...4.23.2)
26
80
 
27
81
  ### Fixed
28
82
 
29
- - Fixed Beaker's behavior when the `strict_host_key_checking` option is
83
+ - Fixed Beaker's behavior when the `strict_host_key_checking` option is
30
84
  provided in the SSH config and Net-SSH > 5 is specified. (#1652)
31
85
 
32
86
  # [4.23.1](https://github.com/puppetlabs/beaker/compare/4.23.0...4.23.1)
data/CODEOWNERS CHANGED
@@ -1,2 +0,0 @@
1
- * @puppetlabs/beaker
2
- * @puppetlabs/dio
data/Rakefile CHANGED
@@ -26,10 +26,7 @@ task :history do
26
26
  Rake::Task['history:gen'].invoke
27
27
  end
28
28
 
29
- task :travis do
30
- Rake::Task['yard'].invoke if !Beaker::Shared::Semvar.version_is_less(RUBY_VERSION, '2.0.0')
31
- Rake::Task['spec'].invoke
32
- end
29
+ task travis: [:yard, :test]
33
30
 
34
31
  module HarnessOptions
35
32
  defaults = {
@@ -18,14 +18,14 @@ Gem::Specification.new do |s|
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.required_ruby_version = Gem::Requirement.new('>= 2.1.8')
21
+ s.required_ruby_version = Gem::Requirement.new('>= 2.4')
22
22
 
23
23
  # Testing dependencies
24
24
  s.add_development_dependency 'rspec', '~> 3.0'
25
25
  s.add_development_dependency 'rspec-its'
26
- s.add_development_dependency 'fakefs', '~> 0.6', '< 0.14.0'
26
+ s.add_development_dependency 'fakefs', '~> 1.2', '< 1.3.0'
27
27
  s.add_development_dependency 'simplecov'
28
- s.add_development_dependency 'rake', '~> 12.0'
28
+ s.add_development_dependency 'rake', '~> 13.0'
29
29
 
30
30
  # Provisioner dependencies - needed for acceptance tests
31
31
  # TODO: figure out how to remove these
@@ -39,13 +39,13 @@ Gem::Specification.new do |s|
39
39
  # Run time dependencies
40
40
  s.add_runtime_dependency 'minitest', '~> 5.4'
41
41
  s.add_runtime_dependency 'minitar', '~> 0.6'
42
- s.add_runtime_dependency 'pry-byebug', '~> 3.6'
42
+ s.add_runtime_dependency 'pry-byebug', '~> 3.9'
43
43
  # pry-byebug can have issues with native readline libs so add rb-readline
44
44
  s.add_runtime_dependency 'rb-readline', '~> 0.5.3'
45
45
 
46
46
  s.add_runtime_dependency 'hocon', '~> 1.0'
47
47
  s.add_runtime_dependency 'net-ssh', '>= 5.0'
48
- s.add_runtime_dependency 'net-scp', '~> 1.2'
48
+ s.add_runtime_dependency 'net-scp', '>= 1.2', '< 4.0'
49
49
  s.add_runtime_dependency 'inifile', '~> 3.0'
50
50
 
51
51
  s.add_runtime_dependency 'rsync', '~> 1.0.9'
@@ -6,7 +6,7 @@ require 'rsync'
6
6
  require 'beaker/dsl/helpers'
7
7
  require 'beaker/dsl/patterns'
8
8
 
9
- [ 'command', 'ssh_connection'].each do |lib|
9
+ [ 'command', 'ssh_connection', 'local_connection' ].each do |lib|
10
10
  require "beaker/#{lib}"
11
11
  end
12
12
 
@@ -19,6 +19,7 @@ module Beaker
19
19
 
20
20
  class CommandFailure < StandardError; end
21
21
  class RebootFailure < CommandFailure; end
22
+ class RebootWarning < StandardError; end
22
23
 
23
24
  # This class provides array syntax for using puppet --configprint on a host
24
25
  class PuppetConfigReader
@@ -294,6 +295,11 @@ module Beaker
294
295
 
295
296
  def connection
296
297
  # create new connection object if necessary
298
+ if self['hypervisor'] == 'none' && @name == 'localhost'
299
+ @connection ||= LocalConnection.connect( { :ssh_env_file => self['ssh_env_file'], :logger => @logger })
300
+ return @connection
301
+ end
302
+
297
303
  @connection ||= SshConnection.connect( { :ip => self['ip'], :vmhostname => self['vmhostname'], :hostname => @name },
298
304
  self['user'],
299
305
  self['ssh'], { :logger => @logger, :ssh_connection_preference => self[:ssh_connection_preference]} )
@@ -22,7 +22,7 @@ module PSWindows::Exec
22
22
  def rm_rf path
23
23
  # ensure that we have the right slashes for windows
24
24
  path = path.gsub(/\//, '\\')
25
- execute("del /s /q #{path}")
25
+ execute(%(del /s /q "#{path}"))
26
26
  end
27
27
 
28
28
  # Move the origin to destination. The destination is removed prior to moving.
@@ -104,9 +104,9 @@ module PSWindows::Exec
104
104
  # @param [String] dir The directory structure to create on the host
105
105
  # @return [Boolean] True, if directory construction succeeded, otherwise False
106
106
  def mkdir_p dir
107
- windows_dirstring = dir.gsub('/','\\')
108
- cmd = "if not exist #{windows_dirstring} (md #{windows_dirstring})"
109
- result = exec(Beaker::Command.new(cmd), :acceptable_exit_codes => [0, 1])
107
+ normalized_path = dir.gsub('/','\\')
108
+ result = exec(powershell("New-Item -Path '#{normalized_path}' -ItemType 'directory'"),
109
+ :acceptable_exit_codes => [0, 1])
110
110
  result.exit_code == 0
111
111
  end
112
112
 
@@ -237,4 +237,19 @@ module PSWindows::Exec
237
237
  end
238
238
  end
239
239
 
240
+ #First path it finds for the command executable
241
+ #@param [String] command The command executable to search for
242
+ #
243
+ # @return [String] Path to the searched executable or empty string if not found
244
+ #
245
+ #@example
246
+ # host.which('ruby')
247
+ def which(command)
248
+ where_command = "cmd /C \"where #{command}\""
249
+
250
+ result = execute(where_command, :accept_all_exit_codes => true)
251
+ return '' if result.empty?
252
+
253
+ result
254
+ end
240
255
  end
@@ -21,11 +21,11 @@ module PSWindows::File
21
21
  end
22
22
 
23
23
  def cat(path)
24
- exec(powershell("type #{path}"))
24
+ exec(powershell("type #{path}")).stdout
25
25
  end
26
26
 
27
27
  def file_exist?(path)
28
- result = exec(Beaker::Command.new("if exist #{path} echo true"), :acceptable_exit_codes => [0, 1])
29
- result.stdout =~ /true/
28
+ result = exec(Beaker::Command.new("if exist #{path} echo true"), accept_all_exit_codes: true)
29
+ result.stdout.strip == 'true'
30
30
  end
31
31
  end
@@ -14,91 +14,104 @@ module Unix::Exec
14
14
  def reboot(wait_time=10, max_connection_tries=9, uptime_retries=18)
15
15
  require 'time'
16
16
 
17
+ attempts = 0
18
+
19
+ # Some systems don't support 'last -F reboot' but it has second granularity
20
+ boot_time_cmd = 'last -F reboot || who -b'
21
+
22
+ # Try to match all of the common formats for 'last' and 'who'
23
+ current_year = Time.now.strftime("%Y")
24
+ boot_time_regex = Regexp.new(%{((?:#{(Date::ABBR_DAYNAMES + Date::ABBR_MONTHNAMES).compact.join('|')}|#{current_year}).+?(\\d+:\\d+)+?(?::(\\d+).+?#{current_year})?)})
25
+
26
+ original_boot_time_str = nil
27
+ original_boot_time_line = nil
17
28
  begin
18
- original_boot_time_str = exec(Beaker::Command.new('who -b'), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
29
+ attempts += 1
30
+ # Number of seconds to sleep before rebooting.
31
+ reboot_sleep = 1
32
+
33
+ original_boot_time_str = exec(Beaker::Command.new(boot_time_cmd), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
19
34
  original_boot_time_line = original_boot_time_str.lines.grep(/boot/).first
20
35
 
21
- raise Beaker::Host::RebootFailure, "Could not find system boot time using 'who -b': #{original_boot_time_str}" unless original_boot_time_line
36
+ raise Beaker::Host::RebootWarning, "Could not find system boot time using '#{boot_time_cmd}': '#{original_boot_time_str}'" unless original_boot_time_line
37
+
38
+ original_boot_time_matches = original_boot_time_line.scan(boot_time_regex).last
39
+
40
+ raise Beaker::Host::RebootWarning, "Found no valid times in '#{original_boot_time_line}'" unless original_boot_time_matches
41
+
42
+ original_boot_time = Time.parse(original_boot_time_matches.first)
43
+
44
+ unless original_boot_time_matches.last
45
+ reboot_sleep = (61 - Time.now.strftime("%S").to_i)
46
+ end
47
+
48
+ @logger.notify("Sleeping #{reboot_sleep} seconds before rebooting")
22
49
 
23
- original_boot_time = Time.parse(original_boot_time_line)
50
+ sleep(reboot_sleep)
24
51
 
25
52
  exec(Beaker::Command.new('/bin/systemctl reboot -i || reboot || /sbin/shutdown -r now'), :expect_connection_failure => true)
26
- rescue Beaker::Host::CommandFailure => e
27
- raise Beaker::Host::RebootFailure, "Command failed when attempting to reboot: #{e.message}"
28
- rescue RuntimeError => e
29
- raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
30
53
  rescue ArgumentError => e
31
54
  raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
55
+ rescue Beaker::Host::RebootWarning => e
56
+ raise if attempts > uptime_retries
57
+ @logger.warn(e.message)
58
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
59
+ retry
60
+ rescue StandardError => e
61
+ raise if attempts > uptime_retries
62
+ @logger.warn("Unexpected Exception: #{e.message}")
63
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
64
+ @logger.warn(e.backtrace[0,3].join("\n"))
65
+ @logger.debug(e.backtrace.join("\n"))
66
+ retry
32
67
  end
33
68
 
34
69
  attempts = 0
35
70
  begin
71
+ attempts += 1
72
+
36
73
  # give the host a little time to shutdown
37
74
  @logger.debug("Waiting #{wait_time} for host to shut down.")
38
75
  sleep wait_time
39
76
 
40
- current_boot_time_str = exec(Beaker::Command.new('who -b'), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
41
- current_boot_time = Time.parse(current_boot_time_str.lines.grep(/boot/).first)
77
+ # Accept all exit codes because this may fail due to the parallel nature of systemd
78
+ current_boot_time_str = exec(Beaker::Command.new(boot_time_cmd), {:max_connection_tries => max_connection_tries, :silent => true, :accept_all_exit_codes => true}).stdout
79
+ current_boot_time_line = current_boot_time_str.lines.grep(/boot/).first
80
+
81
+ raise Beaker::Host::RebootWarning, "Could not find system boot time using '#{boot_time_cmd}': '#{current_boot_time_str}'" unless current_boot_time_line
82
+
83
+ current_boot_time_matches = current_boot_time_line.scan(boot_time_regex).last
84
+
85
+ raise Beaker::Host::RebootWarning, "Found no valid times in '#{current_boot_time_line}'" unless current_boot_time_matches
86
+
87
+ current_boot_time = Time.parse(current_boot_time_matches.first)
42
88
 
43
89
  @logger.debug("Original Boot Time: #{original_boot_time}")
44
90
  @logger.debug("Current Boot Time: #{current_boot_time}")
45
91
 
46
- unless current_boot_time > original_boot_time
92
+ # If this is *exactly* the same then there is really no good way to detect a reboot
93
+ if current_boot_time == original_boot_time
47
94
  raise Beaker::Host::RebootFailure, "Boot time did not reset. Reboot appears to have failed."
48
95
  end
49
- rescue Beaker::Host::RebootFailure => e
50
- attempts += 1
51
- if attempts < uptime_retries
52
- @logger.debug("Boot time did not reset. Will retry #{uptime_retries - attempts} more times.")
53
- retry
54
- else
55
- raise
56
- end
57
- rescue Beaker::Host::CommandFailure => e
58
- raise Beaker::Host::RebootFailure, "Command failed when attempting to reboot: #{e.message}"
59
- rescue RuntimeError => e
60
- raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
61
96
  rescue ArgumentError => e
62
97
  raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
98
+ rescue Beaker::Host::RebootFailure => e
99
+ raise
100
+ rescue Beaker::Host::RebootWarning => e
101
+ raise if attempts > uptime_retries
102
+ @logger.warn(e.message)
103
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
104
+ retry
105
+ rescue StandardError => e
106
+ raise if attempts > uptime_retries
107
+ @logger.warn("Unexpected Exception: #{e.message}")
108
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
109
+ @logger.warn(e.backtrace[0,3].join("\n"))
110
+ @logger.debug(e.backtrace.join("\n"))
111
+ retry
63
112
  end
64
113
  end
65
114
 
66
- def uptime_int(uptime_str)
67
- time_array = uptime_str.split(", ")
68
- accumulated_mins = 0
69
- time_array.each do |time_segment|
70
- value, unit = time_segment.split
71
- if unit.nil?
72
- # 20:47 case: hours & mins
73
- hours, mins = value.split(":")
74
- accumulated_mins += (hours.to_i * 60 + mins.to_i)
75
- elsif unit =~ /day(s)?/
76
- accumulated_mins += (value.to_i * 1440) # 60 * 24 = 1440
77
- elsif unit =~ /min(s)?/
78
- accumulated_mins += value.to_i
79
- else
80
- raise ArgumentError, "can't parse uptime segment: #{time_segment}"
81
- end
82
- end
83
-
84
- accumulated_mins
85
- end
86
-
87
- def parse_uptime(uptime)
88
- # get String from up to users
89
- # eg 19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31
90
- # 8:03 up 52 days, 20:47, 3 users, load averages: 1.36 1.42 1.40
91
- # 22:19 up 54 days, 1 min, 4 users, load averages: 2.08 2.06 2.27
92
- regexp = /.*up (.*)[[:space:]]+[[:digit:]]+ user.*/
93
- result = uptime.match regexp
94
- if self['platform'] =~ /solaris-/ && result[1].empty?
95
- return "0 min"
96
- end
97
- raise "Couldn't parse uptime: #{uptime}" if result.nil?
98
-
99
- result[1].strip.chomp(",")
100
- end
101
-
102
115
  def echo(msg, abs=true)
103
116
  (abs ? '/bin/echo' : 'echo') + " #{msg}"
104
117
  end
@@ -435,4 +448,19 @@ module Unix::Exec
435
448
  true
436
449
  end
437
450
 
451
+ #First path it finds for the command executable
452
+ #@param [String] command The command executable to search for
453
+ #
454
+ # @return [String] Path to the searched executable or empty string if not found
455
+ #
456
+ #@example
457
+ # host.which('ruby')
458
+ def which(command)
459
+ which_command = "which #{command}"
460
+
461
+ result = execute(which_command, :accept_all_exit_codes => true)
462
+ return '' if result.empty?
463
+
464
+ result
465
+ end
438
466
  end
@@ -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