beaker 4.22.0 → 4.24.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f4ee75a711cc94d5814ed063880f947c8529b62bdd992eb5c867af160940655
4
- data.tar.gz: 63d0cdbd54c8693e25b7d2b6b81d26bc8403f1736442624417d9c70674a7d06c
3
+ metadata.gz: a564a311561c124625926ad527bf42a3fe0681e3a0a1c8f7780217a6b00f82dc
4
+ data.tar.gz: 27c0f8cbae1a3f30226cb1dbc08db0cb4464e9e6340114b2b49d13b66e79d587
5
5
  SHA512:
6
- metadata.gz: 990c909cc18aa7634f56a0c98573025c3f4a41f6bd0057936c7086a961a55b3ba7e87bd0e9326316ad86c3d843ac3a637ad48eac95ce8cf2bcf61d647af5f477
7
- data.tar.gz: 85d6f87e4a62e744911e3b616dc3ec34d6e42d1564664d02e9e09dd915163b9c7d30dd66224d274a58a9ae970497b4ea5766a5da4822530300ad15b80b61c44e
6
+ metadata.gz: 2f5c86fd2c82c44dd258d952d404741281a502dc2a2f987014795e940aa7e1f5f56937b628fe62a3fba8105a9f12866bd6a1a1367859f6ab9acbf6b7224d8d42
7
+ data.tar.gz: 095358374f8ff2dfe5c0d95c41d5ff6ac621f69ef95a58580c7151d8f7d13bdbd89e0afa5b70be25ce0f3e14121b56c668765c78705bfe9868233cee37feb687
@@ -20,7 +20,54 @@ 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.22.0...master)
23
+ # [Unreleased](https://github.com/puppetlabs/beaker/compare/4.23.2...master)
24
+
25
+ # [4.24.0](https://github.com/puppetlabs/beaker/compare/4.23.0...4.24.0) - 2020-06-05
26
+
27
+ ### Added
28
+
29
+ - Host method which ([#1651](https://github.com/puppetlabs/beaker/pull/1651))
30
+
31
+ ### Fixed
32
+
33
+ - Fixed implementation for cat and file_exists? host methods for PSWindows ([#1654](https://github.com/puppetlabs/beaker/pull/1654))
34
+ - Fixed implementation for mkdir_p host method for PSWindows ([#1657](https://github.com/puppetlabs/beaker/pull/1657))
35
+
36
+ # [4.23.2](https://github.com/puppetlabs/beaker/compare/4.23.1...4.23.2)
37
+
38
+ ### Fixed
39
+
40
+ - Fixed Beaker's behavior when the `strict_host_key_checking` option is
41
+ provided in the SSH config and Net-SSH > 5 is specified. (#1652)
42
+
43
+ # [4.23.1](https://github.com/puppetlabs/beaker/compare/4.23.0...4.23.1)
44
+
45
+ ### Changed/Removed
46
+
47
+ - Reversed the quoting changes on Unix from #1644 in favor of only quoting on Windows. (#1650)
48
+
49
+ # [4.23.0](https://github.com/puppetlabs/beaker/compare/4.22.1...4.23.0)
50
+
51
+ ### Added
52
+
53
+ - Relaxed dependency on `net-ssh` to `>= 5` to support newer versions. (#1648)
54
+ - `cat` DSL method added. Works on both Unix and Windows hosts. (#1645)
55
+
56
+ ### Changed
57
+
58
+ - The `mkdir_p` and `mv` commands now double quote their file arguments. (#1644) If you rely on file globbing in these methods or elsewhere, please open an issue on the BEAKER project.
59
+ - Change `reboot` method to use `who -b` for uptime detection (#1643)
60
+
61
+ ### Fixed
62
+
63
+ - Use Base64 UTF-16LE encoding for commands (#1626)
64
+ - Fix `tmpdir` method for Powershell on Windows (#1645)
65
+
66
+ # [4.22.1](https://github.com/puppetlabs/beaker/compare/4.22.0...4.22.1)
67
+
68
+ ### Fixed
69
+
70
+ - Removed single quotes around paths for file operation commands on `Host` https://github.com/puppetlabs/beaker/pull/1642
24
71
 
25
72
  # [4.22.0](https://github.com/puppetlabs/beaker/compare/4.21.0...4.22.0) - 2020-05-08
26
73
 
@@ -44,7 +44,7 @@ Gem::Specification.new do |s|
44
44
  s.add_runtime_dependency 'rb-readline', '~> 0.5.3'
45
45
 
46
46
  s.add_runtime_dependency 'hocon', '~> 1.0'
47
- s.add_runtime_dependency 'net-ssh', '~> 5.0'
47
+ s.add_runtime_dependency 'net-ssh', '>= 5.0'
48
48
  s.add_runtime_dependency 'net-scp', '~> 1.2'
49
49
  s.add_runtime_dependency 'inifile', '~> 3.0'
50
50
 
@@ -550,7 +550,6 @@ module Beaker
550
550
  file_contents = nil
551
551
 
552
552
  split_path = win_ads_path(file_path)
553
-
554
553
  if file_exists_on(host, split_path[:path])
555
554
  if host['platform'] =~ /windows/
556
555
  file_path.gsub!('/', '\\')
@@ -14,10 +14,11 @@ module Beaker
14
14
 
15
15
  #Determine is a given URL is accessible
16
16
  #@param [String] link The URL to examine
17
- #@return [Boolean] true if the URL has a '200' HTTP response code, false otherwise
17
+ #@param [Integer] limit redirect limit, will follow redirects that many times
18
+ #@return [Boolean] true if the ultimate URL after following redirects (301&302) has a '200' HTTP response code, false otherwise
18
19
  #@example
19
20
  # extension = link_exists?("#{URL}.tar.gz") ? ".tar.gz" : ".tar"
20
- def link_exists?(link)
21
+ def link_exists?(link, limit=10)
21
22
  begin
22
23
  require "net/http"
23
24
  require "net/https"
@@ -26,8 +27,12 @@ module Beaker
26
27
  http = Net::HTTP.new(url.host, url.port)
27
28
  http.use_ssl = (url.scheme == 'https')
28
29
  http.verify_mode = (OpenSSL::SSL::VERIFY_NONE)
29
- http.start do |http|
30
- return http.head(url.request_uri).code == "200"
30
+ response = http.start { |http| http.head(url.request_uri) }
31
+ if (['301', '302'].include? response.code) && limit > 0
32
+ logger.debug("#{__method__} following #{response.code} to #{response['location']}")
33
+ link_exists?(response['location'], limit - 1)
34
+ else
35
+ response.code == "200"
31
36
  end
32
37
  rescue
33
38
  return false
@@ -75,18 +75,14 @@ module Beaker
75
75
  Command.new("powershell.exe", ps_args)
76
76
  end
77
77
 
78
- # Convert the provided command string to Base64
78
+ # Convert the provided command string to Base64 encoded UTF-16LE command
79
79
  # @param [String] cmd The command to convert to Base64
80
80
  # @return [String] The converted string
81
81
  # @api private
82
82
  def encode_command(cmd)
83
- cmd = cmd.chars.to_a.join("\x00").chomp
84
- cmd << "\x00" unless cmd[-1].eql? "\x00"
85
83
  # use strict_encode because linefeeds are not correctly handled in our model
86
- cmd = Base64.strict_encode64(cmd).chomp
87
- cmd
84
+ Base64.strict_encode64(cmd.encode(Encoding::UTF_16LE)).chomp
88
85
  end
89
-
90
86
  end
91
87
  end
92
88
  end
@@ -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
@@ -7,16 +7,25 @@ module PSWindows::File
7
7
  end
8
8
 
9
9
  def tmpdir(name = '')
10
- result = exec(powershell('[System.IO.Path]::GetTempPath()'))
11
- result.stdout.chomp()
10
+ tmp_path = exec(powershell('[System.IO.Path]::GetTempPath()')).stdout.chomp()
11
+
12
+ if name == ''
13
+ name = exec(powershell('[System.IO.Path]::GetRandomFileName()')).stdout.chomp()
14
+ end
15
+ exec(powershell("New-Item -Path '#{tmp_path}' -Force -Name '#{name}' -ItemType 'directory'"))
16
+ File.join(tmp_path, name)
12
17
  end
13
18
 
14
19
  def path_split(paths)
15
20
  paths.split(';')
16
21
  end
17
22
 
23
+ def cat(path)
24
+ exec(powershell("type #{path}")).stdout
25
+ end
26
+
18
27
  def file_exist?(path)
19
- result = exec(Beaker::Command.new("if exist #{path} echo true"), :acceptable_exit_codes => [0, 1])
20
- result.stdout =~ /true/
28
+ result = exec(Beaker::Command.new("if exist #{path} echo true"), accept_all_exit_codes: true)
29
+ result.stdout.strip == 'true'
21
30
  end
22
31
  end
@@ -12,20 +12,23 @@ module Unix::Exec
12
12
  #
13
13
  # Will throw an exception RebootFailure if it fails
14
14
  def reboot(wait_time=10, max_connection_tries=9, uptime_retries=18)
15
+ require 'time'
16
+
15
17
  begin
16
- original_uptime = exec(Beaker::Command.new("uptime"))
17
- original_uptime_str = parse_uptime original_uptime.stdout
18
- original_uptime_int = uptime_int original_uptime_str
18
+ original_boot_time_str = exec(Beaker::Command.new('who -b'), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
19
+ original_boot_time_line = original_boot_time_str.lines.grep(/boot/).first
19
20
 
20
- if self['platform'] =~ /solaris/
21
- exec(Beaker::Command.new("reboot"), :expect_connection_failure => true)
22
- else
23
- exec(Beaker::Command.new("/sbin/shutdown -r now"), :expect_connection_failure => true)
24
- end
21
+ raise Beaker::Host::RebootFailure, "Could not find system boot time using 'who -b': #{original_boot_time_str}" unless original_boot_time_line
22
+
23
+ original_boot_time = Time.parse(original_boot_time_line)
24
+
25
+ exec(Beaker::Command.new('/bin/systemctl reboot -i || reboot || /sbin/shutdown -r now'), :expect_connection_failure => true)
25
26
  rescue Beaker::Host::CommandFailure => e
26
27
  raise Beaker::Host::RebootFailure, "Command failed when attempting to reboot: #{e.message}"
27
28
  rescue RuntimeError => e
28
29
  raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
30
+ rescue ArgumentError => e
31
+ raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
29
32
  end
30
33
 
31
34
  attempts = 0
@@ -34,18 +37,19 @@ module Unix::Exec
34
37
  @logger.debug("Waiting #{wait_time} for host to shut down.")
35
38
  sleep wait_time
36
39
 
37
- #use uptime to check if the host has rebooted
38
- current_uptime_exec = exec(Beaker::Command.new("uptime"), {:max_connection_tries => max_connection_tries, :silent => true})
39
- current_uptime = current_uptime_exec.stdout
40
- current_uptime_str = parse_uptime current_uptime
41
- current_uptime_int = uptime_int current_uptime_str
42
- unless original_uptime_int > current_uptime_int
43
- raise Beaker::Host::RebootFailure, "Uptime did not reset. Reboot appears to have failed."
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)
42
+
43
+ @logger.debug("Original Boot Time: #{original_boot_time}")
44
+ @logger.debug("Current Boot Time: #{current_boot_time}")
45
+
46
+ unless current_boot_time > original_boot_time
47
+ raise Beaker::Host::RebootFailure, "Boot time did not reset. Reboot appears to have failed."
44
48
  end
45
49
  rescue Beaker::Host::RebootFailure => e
46
50
  attempts += 1
47
51
  if attempts < uptime_retries
48
- @logger.debug("Uptime did not reset. Will retry #{uptime_retries - attempts} more times.")
52
+ @logger.debug("Boot time did not reset. Will retry #{uptime_retries - attempts} more times.")
49
53
  retry
50
54
  else
51
55
  raise
@@ -54,45 +58,11 @@ module Unix::Exec
54
58
  raise Beaker::Host::RebootFailure, "Command failed when attempting to reboot: #{e.message}"
55
59
  rescue RuntimeError => e
56
60
  raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
61
+ rescue ArgumentError => e
62
+ raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
57
63
  end
58
64
  end
59
65
 
60
- def uptime_int(uptime_str)
61
- time_array = uptime_str.split(", ")
62
- accumulated_mins = 0
63
- time_array.each do |time_segment|
64
- value, unit = time_segment.split
65
- if unit.nil?
66
- # 20:47 case: hours & mins
67
- hours, mins = value.split(":")
68
- accumulated_mins += (hours.to_i * 60 + mins.to_i)
69
- elsif unit =~ /day(s)?/
70
- accumulated_mins += (value.to_i * 1440) # 60 * 24 = 1440
71
- elsif unit =~ /min(s)?/
72
- accumulated_mins += value.to_i
73
- else
74
- raise ArgumentError, "can't parse uptime segment: #{time_segment}"
75
- end
76
- end
77
-
78
- accumulated_mins
79
- end
80
-
81
- def parse_uptime(uptime)
82
- # get String from up to users
83
- # eg 19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31
84
- # 8:03 up 52 days, 20:47, 3 users, load averages: 1.36 1.42 1.40
85
- # 22:19 up 54 days, 1 min, 4 users, load averages: 2.08 2.06 2.27
86
- regexp = /.*up (.*)[[:space:]]+[[:digit:]]+ user.*/
87
- result = uptime.match regexp
88
- if self['platform'] =~ /solaris-/ && result[1].empty?
89
- return "0 min"
90
- end
91
- raise "Couldn't parse uptime: #{uptime}" if result.nil?
92
-
93
- result[1].strip.chomp(",")
94
- end
95
-
96
66
  def echo(msg, abs=true)
97
67
  (abs ? '/bin/echo' : 'echo') + " #{msg}"
98
68
  end
@@ -127,7 +97,7 @@ module Unix::Exec
127
97
  # @param [String] dir The directory structure to create on the host
128
98
  # @return [Boolean] True, if directory construction succeeded, otherwise False
129
99
  def mkdir_p dir
130
- cmd = "mkdir -p '#{dir}'"
100
+ cmd = "mkdir -p #{dir}"
131
101
  result = exec(Beaker::Command.new(cmd), :acceptable_exit_codes => [0, 1])
132
102
  result.exit_code == 0
133
103
  end
@@ -135,7 +105,7 @@ module Unix::Exec
135
105
  # Recursively remove the path provided
136
106
  # @param [String] path The path to remove
137
107
  def rm_rf path
138
- execute("rm -rf '#{path}'")
108
+ execute("rm -rf #{path}")
139
109
  end
140
110
 
141
111
  # Move the origin to destination. The destination is removed prior to moving.
@@ -144,7 +114,7 @@ module Unix::Exec
144
114
  # @param [Boolean] rm Remove the destination prior to move
145
115
  def mv orig, dest, rm=true
146
116
  rm_rf dest unless !rm
147
- execute("mv '#{orig}' '#{dest}'")
117
+ execute("mv #{orig} #{dest}")
148
118
  end
149
119
 
150
120
  # Attempt to ping the provided target hostname
@@ -429,4 +399,19 @@ module Unix::Exec
429
399
  true
430
400
  end
431
401
 
402
+ #First path it finds for the command executable
403
+ #@param [String] command The command executable to search for
404
+ #
405
+ # @return [String] Path to the searched executable or empty string if not found
406
+ #
407
+ #@example
408
+ # host.which('ruby')
409
+ def which(command)
410
+ which_command = "which #{command}"
411
+
412
+ result = execute(which_command, :accept_all_exit_codes => true)
413
+ return '' if result.empty?
414
+
415
+ result
416
+ end
432
417
  end
@@ -58,6 +58,10 @@ module Unix::File
58
58
  execute("ls -ld #{path}")
59
59
  end
60
60
 
61
+ def cat(path)
62
+ execute("cat #{path}")
63
+ end
64
+
61
65
  # Handles any changes needed in a path for SCP
62
66
  #
63
67
  # @param [String] path File path to SCP to
@@ -119,6 +119,25 @@ module Windows::Exec
119
119
  false
120
120
  end
121
121
 
122
+ # Create the provided directory structure on the host
123
+ # @param [String] dir The directory structure to create on the host
124
+ # @return [Boolean] True, if directory construction succeeded, otherwise False
125
+ def mkdir_p dir
126
+ cmd = "mkdir -p \"#{dir}\""
127
+ result = exec(Beaker::Command.new(cmd), :acceptable_exit_codes => [0, 1])
128
+ result.exit_code == 0
129
+ end
130
+
131
+ # Move the origin to destination. The destination is removed prior to moving.
132
+ # @param [String] orig The origin path
133
+ # @param [String] dest the destination path
134
+ # @param [Boolean] rm Remove the destination prior to move
135
+ def mv orig, dest, rm=true
136
+ rm_rf dest unless !rm
137
+ execute("mv \"#{orig}\" \"#{dest}\"")
138
+ end
139
+
140
+
122
141
  # Determine if cygwin is actually installed on the SUT. Differs from
123
142
  # is_cygwin?, which is just a type check for a Windows::Host.
124
143
  #
@@ -68,6 +68,11 @@ module Beaker
68
68
  max_connection_tries = options[:max_connection_tries] || 11
69
69
  begin
70
70
  @logger.debug "Attempting ssh connection to #{host}, user: #{user}, opts: #{ssh_opts}"
71
+
72
+ 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
+ end
75
+
71
76
  Net::SSH.start(host, user, ssh_opts)
72
77
  rescue *RETRYABLE_EXCEPTIONS => e
73
78
  if try <= max_connection_tries
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '4.22.0'
3
+ STRING = '4.24.0'
4
4
  end
5
5
  end
@@ -99,5 +99,59 @@ module Beaker
99
99
  to be == "set \"LD_PATH=/:/tmp\" && "
100
100
  end
101
101
  end
102
+
103
+ describe '#which' do
104
+ before do
105
+ allow(instance).to receive(:execute)
106
+ .with(where_command, :accept_all_exit_codes => true).and_return(result)
107
+ end
108
+ let(:where_command) { "cmd /C \"where ruby\"" }
109
+
110
+ context 'when only the environment variable PATH is used' do
111
+ let(:result) { "C:\\Ruby26-x64\\bin\\ruby.exe" }
112
+
113
+ it 'returns the correct path' do
114
+ response = instance.which('ruby')
115
+
116
+ expect(response).to eq(result)
117
+ end
118
+ end
119
+
120
+ context 'when command is not found' do
121
+ let(:where_command) { "cmd /C \"where unknown\"" }
122
+ let(:result) { '' }
123
+
124
+ it 'return empty string if command is not found' do
125
+ response = instance.which('unknown')
126
+
127
+ expect(response).to eq(result)
128
+ end
129
+ end
130
+ end
131
+
132
+ describe '#mkdir_p' do
133
+ let(:dir_path) { "C:\\tmpdir\\my_dir" }
134
+ let(:beaker_command) { instance_spy(Beaker::Command) }
135
+ let(:command) {"-Command New-Item -Path '#{dir_path}' -ItemType 'directory'"}
136
+ let(:result) { instance_spy(Beaker::Result) }
137
+
138
+ before do
139
+ allow(Beaker::Command).to receive(:new).
140
+ with('powershell.exe', array_including(command)).and_return(beaker_command)
141
+ allow(instance).to receive(:exec).with(beaker_command, :acceptable_exit_codes => [0, 1]).and_return(result)
142
+ end
143
+
144
+ it 'returns true and creates folder structure' do
145
+ allow(result).to receive(:exit_code).and_return(0)
146
+
147
+ expect(instance.mkdir_p(dir_path)).to be(true)
148
+ end
149
+
150
+ it 'returns false if failed to create directory structure' do
151
+ allow(result).to receive(:exit_code).and_return(1)
152
+
153
+ expect(instance.mkdir_p(dir_path)).to be(false)
154
+ end
155
+ end
102
156
  end
103
157
  end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ module Beaker
4
+ describe PSWindows::File do
5
+ class PSWindowsFileTest
6
+ include PSWindows::File
7
+ include Beaker::DSL::Wrappers
8
+
9
+ def initialize(hash, logger)
10
+ @hash = hash
11
+ @logger = logger
12
+ end
13
+
14
+ def [](k)
15
+ @hash[k]
16
+ end
17
+
18
+ def to_s
19
+ "me"
20
+ end
21
+ end
22
+
23
+ let (:opts) { @opts || {} }
24
+ let (:logger) { double( 'logger' ).as_null_object }
25
+ let (:instance) { PSWindowsFileTest.new(opts, logger) }
26
+
27
+ describe '#cat' do
28
+ let(:path) { '/path/to/cat' }
29
+ let(:content) { 'file content' }
30
+ it 'reads output for file' do
31
+ expect(instance).to receive(:exec).and_return(double(stdout: content))
32
+ expect(Beaker::Command).to receive(:new).with('powershell.exe', array_including("-Command type #{path}"))
33
+ expect(instance.cat(path)).to eq(content)
34
+ end
35
+ end
36
+
37
+ describe '#file_exist?' do
38
+ let(:path) { '/path/to/test/file.txt' }
39
+ context 'file exists' do
40
+ it 'returns true' do
41
+ expect(instance).to receive(:exec).and_return(double(stdout: "true\n"))
42
+ expect(Beaker::Command).to receive(:new).with("if exist #{path} echo true")
43
+ expect(instance.file_exist?(path)).to eq(true)
44
+ end
45
+ end
46
+
47
+ context 'file does not exist' do
48
+ it 'returns false' do
49
+ expect(instance).to receive(:exec).and_return(double(stdout: ""))
50
+ expect(Beaker::Command).to receive(:new).with("if exist #{path} echo true")
51
+ expect(instance.file_exist?(path)).to eq(false)
52
+ end
53
+ end
54
+ end
55
+
56
+ describe '#tmpdir' do
57
+ let(:tmp_path) { 'C:\\tmpdir\\' }
58
+ let(:fake_command) { Beaker::Command.new('command1') }
59
+
60
+ before do
61
+ allow(instance).to receive(:execute).with(anything)
62
+ end
63
+
64
+ context 'with dirname sent' do
65
+ let(:name) { 'my_dir' }
66
+ it 'returns the path to my_dir' do
67
+ expect(Beaker::Command).to receive(:new).
68
+ with('powershell.exe', array_including('-Command [System.IO.Path]::GetTempPath()')).
69
+ and_return(fake_command)
70
+ expect(instance).to receive(:exec).with(instance_of(Beaker::Command)).and_return(double(stdout: tmp_path))
71
+
72
+ expect(Beaker::Command).to receive(:new).
73
+ with('powershell.exe', array_including("-Command New-Item -Path '#{tmp_path}' -Force -Name '#{name}' -ItemType 'directory'")).
74
+ and_return(fake_command)
75
+ expect(instance).to receive(:exec).with(instance_of(Beaker::Command)).and_return(true)
76
+
77
+ expect(instance.tmpdir(name)).to eq(File.join(tmp_path, name))
78
+ end
79
+ end
80
+
81
+ context 'without dirname sent' do
82
+ let(:name) { '' }
83
+ let(:random_dir) { 'dirname' }
84
+
85
+ it 'returns the path to random name dir' do
86
+ expect(Beaker::Command).to receive(:new).
87
+ with('powershell.exe', array_including('-Command [System.IO.Path]::GetTempPath()')).
88
+ and_return(fake_command)
89
+ expect(instance).to receive(:exec).with(instance_of(Beaker::Command)).and_return(double(stdout: tmp_path))
90
+
91
+ expect(Beaker::Command).to receive(:new).
92
+ with('powershell.exe', array_including('-Command [System.IO.Path]::GetRandomFileName()')).
93
+ and_return(fake_command)
94
+ expect(instance).to receive(:exec).with(instance_of(Beaker::Command)).and_return(double(stdout: random_dir))
95
+
96
+ expect(Beaker::Command).to receive(:new).
97
+ with('powershell.exe', array_including("-Command New-Item -Path '#{tmp_path}' -Force -Name '#{random_dir}' -ItemType 'directory'")).
98
+ and_return(fake_command)
99
+ expect(instance).to receive(:exec).with(instance_of(Beaker::Command)).and_return(true)
100
+
101
+ expect(instance.tmpdir).to eq(File.join(tmp_path, random_dir))
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -29,7 +29,7 @@ module Beaker
29
29
 
30
30
  it "deletes" do
31
31
  path = '/path/to/delete'
32
- expect( instance ).to receive(:execute).with("rm -rf '#{path}'").and_return(0)
32
+ expect( instance ).to receive(:execute).with("rm -rf #{path}").and_return(0)
33
33
  expect( instance.rm_rf(path) ).to be === 0
34
34
  end
35
35
  end
@@ -39,14 +39,14 @@ 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("rm -rf '#{destination}'").and_return(0)
43
- expect( instance ).to receive(:execute).with("mv '#{origin}' '#{destination}'").and_return(0)
42
+ expect( instance ).to receive(:execute).with("rm -rf #{destination}").and_return(0)
43
+ expect( instance ).to receive(:execute).with("mv #{origin} #{destination}").and_return(0)
44
44
  expect( instance.mv(origin, destination) ).to be === 0
45
45
 
46
46
  end
47
47
 
48
48
  it 'does not rm' do
49
- expect( instance ).to receive(:execute).with("mv '#{origin}' '#{destination}'").and_return(0)
49
+ expect( instance ).to receive(:execute).with("mv #{origin} #{destination}").and_return(0)
50
50
  expect( instance.mv(origin, destination, false) ).to be === 0
51
51
  end
52
52
  end
@@ -171,94 +171,135 @@ module Beaker
171
171
  describe '#reboot' do
172
172
  # no-op response
173
173
  let (:response) { double( 'response' ) }
174
- let (:uptime_initial_response) { double( 'response' ) }
175
- let (:uptime_initial_stdout) { '19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31' }
174
+ let (:boot_time_initial_response) { double( 'response' ) }
176
175
 
177
- let (:uptime_success_response) { double( 'response' ) }
178
- let (:uptime_success_stdout) { '19:52 up 0 mins, 2 users, load averages: 2.95 4.19 4.31' }
176
+ let (:boot_time_success_response) { double( 'response' ) }
179
177
  let (:sleep_time) { 10 }
180
178
 
181
179
  before :each do
182
- # stubs enough to survive the first uptime call & output parsing
180
+ # stubs enough to survive the first boot_time call & output parsing
183
181
  # note: just stubs input-chain between calls, parsing methods still run
184
- allow(Beaker::Command).to receive(:new).with("uptime").and_return(:uptime_command_stub)
182
+ allow(Beaker::Command).to receive(:new).with('who -b').and_return(:boot_time_command_stub)
185
183
 
186
- # mock initial uptime call
187
- allow(instance).to receive( :exec ).with(:uptime_command_stub).and_return(uptime_initial_response).once
188
- allow(uptime_initial_response).to receive(:stdout).and_return(uptime_initial_stdout)
189
-
190
- allow(uptime_success_response).to receive(:stdout).and_return(uptime_success_stdout)
184
+ allow(boot_time_initial_response).to receive(:stdout).and_return(boot_time_initial_stdout)
185
+ allow(boot_time_success_response).to receive(:stdout).and_return(boot_time_success_stdout)
191
186
 
192
187
  allow(instance).to receive(:sleep)
193
188
 
194
- allow(Beaker::Command).to receive(:new).with("/sbin/shutdown -r now").and_return(:shutdown_command_stub)
189
+ allow(Beaker::Command).to receive(:new).with("/bin/systemctl reboot -i || reboot || /sbin/shutdown -r now").and_return(:shutdown_command_stub)
195
190
  end
196
191
 
197
- it 'raises a reboot failure when command fails' do
198
- expect(instance).not_to receive(:sleep)
199
- expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Host::CommandFailure).once
200
- expect{ instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Command failed when attempting to reboot: .*/)
201
- end
192
+ 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' }
202
195
 
203
- it 'raises a reboot failure when we receive an unexpected error' do
204
- expect(instance).not_to receive(:sleep)
205
- expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Net::SSH::HostKeyError).once
206
- expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unexpected exception in reboot: .*/)
207
- end
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
208
203
 
209
- it 'raises RebootFailure if new uptime is never less than old uptime' do
210
- expect(instance).to receive(:sleep).with(sleep_time)
211
- # bypass shutdown command itself
212
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
213
- # allow the second uptime and the hash arguments in exec, repeated 18 times by default in order to replicate the previous behavior of the ping based Host.down?
214
- expect(instance).to receive( :exec ).with(:uptime_command_stub, anything).and_return(uptime_initial_response).exactly(18).times
204
+ expect(instance.reboot).to be(nil)
205
+ end
215
206
 
216
- expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Uptime did not reset/)
217
- end
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
218
214
 
219
- it 'raises RebootFailure if new uptime is never less than old uptime when the number of retries is changed' do
220
- expect(instance).to receive(:sleep).with(sleep_time)
221
- # bypass shutdown command itself
222
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
223
- # allow the second uptime and the hash arguments in exec, repeated 10 times by default
224
- expect(instance).to receive( :exec ).with(:uptime_command_stub, anything).and_return(uptime_initial_response).exactly(10).times
215
+ expect(instance.reboot(10)).to be(nil)
216
+ end
225
217
 
226
- expect { instance.reboot(wait_time=sleep_time, max_connection_tries=9, uptime_retries=10) }.to raise_error(Beaker::Host::RebootFailure, /Uptime did not reset/)
227
- end
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
225
+
226
+ expect(instance.reboot(sleep_time, 20)).to be(nil)
227
+ end
228
+
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
233
+
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
237
+
238
+ expect{ instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Command failed when attempting to reboot: .*/)
239
+ end
240
+
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
244
+
245
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unexpected exception in reboot: .*/)
246
+ end
228
247
 
229
- it 'passes if new uptime is less than old uptime' do
230
- expect(instance).to receive(:sleep).with(sleep_time)
231
- # bypass shutdown command itself
232
- expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
233
- # allow the second uptime and the hash arguments in exec
234
- expect(instance).to receive( :exec ).with(:uptime_command_stub, anything).and_return(uptime_success_response).once
248
+ context 'incorrect time string' do
249
+ context 'original time' do
250
+ let (:boot_time_initial_stdout) { 'boot bad' }
235
251
 
236
- expect(instance.reboot).to be(nil)
252
+ it 'raises a reboot failure' do
253
+ expect(instance).not_to receive(:sleep)
254
+
255
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unable to parse time: .*/)
256
+ end
257
+ end
258
+
259
+ context 'current time' do
260
+ let (:boot_time_success_stdout) { 'boot bad' }
261
+
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
267
+
268
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unable to parse time: .*/)
269
+ end
270
+ end
271
+ end
272
+ end
237
273
  end
238
274
 
239
- context 'with wait_time_parameter' do
240
- it 'passes if new uptime is less than old uptime' do
241
- expect(instance).to receive(:sleep).with(10)
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' }
278
+
279
+ it 'raises RebootFailure' do
280
+ expect(instance).to receive(:sleep).with(sleep_time)
242
281
  # bypass shutdown command itself
243
282
  expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
244
- # allow the second uptime and the hash arguments in exec
245
- expect(instance).to receive( :exec ).with(:uptime_command_stub, anything).and_return(uptime_success_response).once
246
283
 
247
- expect(instance.reboot(10)).to be(nil)
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
287
+
288
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Boot time did not reset/)
248
289
  end
249
- end
250
290
 
251
- context 'with max_connection_tries parameter' do
252
- it 'passes if new uptime is less than old uptime' do
291
+ it 'raises RebootFailure if the number of retries is changed' do
253
292
  expect(instance).to receive(:sleep).with(sleep_time)
254
293
  # bypass shutdown command itself
255
294
  expect(instance).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response).once
256
- # allow the second uptime and the hash arguments in exec
257
- expect(instance).to receive( :exec ).with(:uptime_command_stub, hash_including(:max_connection_tries => 20)).and_return(uptime_success_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
258
298
 
259
- expect(instance.reboot(sleep_time, 20)).to be(nil)
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/)
260
300
  end
261
301
  end
302
+
262
303
  end
263
304
 
264
305
  describe '#enable_remote_rsyslog' do
@@ -272,40 +313,32 @@ module Beaker
272
313
 
273
314
  end
274
315
 
275
- describe '#parse_uptime' do
276
- it 'parses variation of uptime string' do
277
- expect(instance.parse_uptime("19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31")).to be == "14 mins"
278
- end
279
- it 'parses variation 2 of uptime string' do
280
- expect(instance.parse_uptime("8:03 up 52 days, 20:47, 3 users, load averages: 1.36 1.42 1.40")).to be == "52 days, 20:47"
281
- end
282
- it 'parses variation 3 of uptime string' do
283
- expect(instance.parse_uptime("22:19 up 54 days, 1 min, 4 users, load averages: 2.08 2.06 2.27")).to be == "54 days, 1 min"
284
- end
285
- it 'parses variation 4 of uptime string' do
286
- expect(instance.parse_uptime("18:44:45 up 5 min, 0 users, load average: 0.14, 0.11, 0.05")).to be == "5 min"
316
+ describe '#which' do
317
+ before do
318
+ allow(instance).to receive(:execute)
319
+ .with(where_command, :accept_all_exit_codes => true).and_return(result)
287
320
  end
288
- it 'parses solaris\'s "just up" without time message' do
289
- opts['platform'] = 'solaris-11-x86_64'
290
- expect(instance.parse_uptime("10:05am up 0 users, load average: 0.66, 0.14, 0.05")).to be == "0 min"
291
- end
292
- end
293
321
 
294
- describe '#uptime_int' do
295
- it 'parses time segment variation into a minute value' do
296
- expect(instance.uptime_int("14 mins")).to be == 14
297
- end
298
- it 'parses time segment variation 2 into a minute value' do
299
- expect(instance.uptime_int("52 days, 20:47")).to be == 76127
300
- end
301
- it 'parses time segment variation 3 into a minute value' do
302
- expect(instance.uptime_int("54 days, 1 min")).to be == 77761
322
+ context 'when only the environment variable PATH is used' do
323
+ let(:where_command) { "which ruby" }
324
+ let(:result) { "/usr/bin/ruby.exe" }
325
+
326
+ it 'returns the correct path' do
327
+ response = instance.which('ruby')
328
+
329
+ expect(response).to eq(result)
303
330
  end
304
- it 'parses time segment variation 4 into a minute value' do
305
- expect(instance.uptime_int("54 days")).to be == 77760
306
331
  end
307
- it 'raises if we pass garbage to it' do
308
- expect { instance.uptime_int("solaris roxx my soxx") }.to raise_error
332
+
333
+ context 'when command is not found' do
334
+ let(:where_command) { "which unknown" }
335
+ let(:result) { '' }
336
+
337
+ it 'return empty string if command is not found' do
338
+ response = instance.which('unknown')
339
+
340
+ expect(response).to eq(result)
341
+ end
309
342
  end
310
343
  end
311
344
  end
@@ -196,6 +196,14 @@ module Beaker
196
196
  end
197
197
  end
198
198
 
199
+ describe '#cat' do
200
+ let (:path) { '/path/to/cat/on' }
201
+ it 'calls cat for path' do
202
+ expect( instance ).to receive( :execute ).with( "cat #{path}" ).and_return( 0 )
203
+ expect( instance.cat( path ) ).to be === 0
204
+ end
205
+ end
206
+
199
207
  describe '#chmod' do
200
208
  context 'not recursive' do
201
209
  it 'calls execute with chmod' do
@@ -3,6 +3,7 @@ require 'spec_helper'
3
3
  module Beaker
4
4
  describe Windows::Exec do
5
5
  class WindowsExecTest
6
+ include Unix::Exec
6
7
  include Windows::Exec
7
8
 
8
9
  def initialize(hash, logger)
@@ -78,5 +79,22 @@ module Beaker
78
79
  expect(instance.cygwin_installed?).to eq(false)
79
80
  end
80
81
  end
82
+
83
+ context 'mv' do
84
+ let(:origin) { '/origin/path/of/content' }
85
+ let(:destination) { '/destination/path/of/content' }
86
+
87
+ it 'rm first' do
88
+ expect( instance ).to receive(:execute).with("rm -rf #{destination}").and_return(0)
89
+ expect( instance ).to receive(:execute).with("mv \"#{origin}\" \"#{destination}\"").and_return(0)
90
+ expect( instance.mv(origin, destination) ).to be === 0
91
+
92
+ end
93
+
94
+ it 'does not rm' do
95
+ expect( instance ).to receive(:execute).with("mv \"#{origin}\" \"#{destination}\"").and_return(0)
96
+ expect( instance.mv(origin, destination, false) ).to be === 0
97
+ end
98
+ end
81
99
  end
82
100
  end
@@ -325,7 +325,7 @@ module Beaker
325
325
  allow( result ).to receive( :exit_code ).and_return( 0 )
326
326
  allow( host ).to receive( :exec ).and_return( result )
327
327
 
328
- expect( Beaker::Command ).to receive(:new).with("mkdir -p 'test/test/test'")
328
+ expect( Beaker::Command ).to receive(:new).with("mkdir -p \"test/test/test\"")
329
329
  expect( host.mkdir_p('test/test/test') ).to be == true
330
330
 
331
331
  end
@@ -337,7 +337,7 @@ module Beaker
337
337
  allow( result ).to receive( :exit_code ).and_return( 0 )
338
338
  allow( host ).to receive( :exec ).and_return( result )
339
339
 
340
- expect( Beaker::Command ).to receive(:new).with("mkdir -p 'test/test/test'")
340
+ expect( Beaker::Command ).to receive(:new).with("mkdir -p \"test/test/test\"")
341
341
  expect( host.mkdir_p('test/test/test') ).to be == true
342
342
 
343
343
  end
@@ -349,7 +349,13 @@ module Beaker
349
349
  allow( result ).to receive( :exit_code ).and_return( 0 )
350
350
  allow( host ).to receive( :exec ).and_return( result )
351
351
 
352
- expect( Beaker::Command ).to receive(:new).with("if not exist test\\test\\test (md test\\test\\test)")
352
+ expect( Beaker::Command ).to receive(:new).
353
+ with("powershell.exe", ["-ExecutionPolicy Bypass",
354
+ "-InputFormat None",
355
+ "-NoLogo",
356
+ "-NoProfile",
357
+ "-NonInteractive",
358
+ "-Command New-Item -Path 'test\\test\\test' -ItemType 'directory'"])
353
359
  expect( host.mkdir_p('test/test/test') ).to be == true
354
360
 
355
361
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beaker
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.22.0
4
+ version: 4.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-08 00:00:00.000000000 Z
11
+ date: 2020-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -216,14 +216,14 @@ dependencies:
216
216
  name: net-ssh
217
217
  requirement: !ruby/object:Gem::Requirement
218
218
  requirements:
219
- - - "~>"
219
+ - - ">="
220
220
  - !ruby/object:Gem::Version
221
221
  version: '5.0'
222
222
  type: :runtime
223
223
  prerelease: false
224
224
  version_requirements: !ruby/object:Gem::Requirement
225
225
  requirements:
226
- - - "~>"
226
+ - - ">="
227
227
  - !ruby/object:Gem::Version
228
228
  version: '5.0'
229
229
  - !ruby/object:Gem::Dependency
@@ -630,6 +630,7 @@ files:
630
630
  - spec/beaker/host/mac/user_spec.rb
631
631
  - spec/beaker/host/mac_spec.rb
632
632
  - spec/beaker/host/pswindows/exec_spec.rb
633
+ - spec/beaker/host/pswindows/file_spec.rb
633
634
  - spec/beaker/host/pswindows/user_spec.rb
634
635
  - spec/beaker/host/pswindows_spec.rb
635
636
  - spec/beaker/host/unix/exec_spec.rb