beaker 4.23.2 → 4.27.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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