beaker 4.22.0 → 4.24.0

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: 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