beaker 4.15.0 → 4.16.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: 202dc17b3f60a07a9a96bd16a252cbca228cca4ae969fc38595e2f1e0806415d
4
- data.tar.gz: 99d2385877a317dac33c2500fab84ede0a6890dd64a3b29015e50c5c0230bee4
3
+ metadata.gz: 47866cdc1343ec3c973731ac22f559a8c8bb9191a28b5e0fa502b0161dc77869
4
+ data.tar.gz: 2120cec9e0a21b849ec959456ccb5c48fefd9f0816ad219cc5e82317147ce53e
5
5
  SHA512:
6
- metadata.gz: 9658a076c692a948f6d56b18ae975d02064179d51c9500c83b6acd60cc8a777234fccf573ac068c3ccdff19d481658beee28506f890ea9af03558eaa3d4484ba
7
- data.tar.gz: efa9b80bae656f0daf6c53c9ac9d5e07f1a537b72bfe1a10d6fb66fa4c8e545e5a7a38a82bf9da121a93a194ed5e89f7ec53a033d412da1d936320e74927fdf6
6
+ metadata.gz: c351c5016c60b958a81bb4ab75a01f059ec679690b9c2e72d0664b45cb2cba3a98aa765be65c81f8518dfa6e84bc59fc959c3ac940641050f000799014d0b35f
7
+ data.tar.gz: 04935abd0b44d9bb222548af8281ff5b9ee0694ede3e97c91df6c77ca31ef242d9fed732eb52b4f658c7182a6f2e886c99eeac34ce2f6551156cadf31ccace80
@@ -11,7 +11,18 @@ Tracking in this Changelog began for this project in version 3.25.0.
11
11
  If you're looking for changes from before this, refer to the project's
12
12
  git logs & PR history.
13
13
 
14
- # [Unreleased](https://github.com/puppetlabs/beaker/compare/4.15.0...master)
14
+ # [Unreleased](https://github.com/puppetlabs/beaker/compare/4.16.0...master)
15
+
16
+ # [4.16.0](https://github.com/puppetlabs/beaker/compare/4.15.0...4.16.0) - 2020-02-05
17
+
18
+ ### Added
19
+
20
+ - release section to README ([#1618](https://github.com/puppetlabs/beaker/pull/1618))
21
+ - false return if `link_exists?` raises an error ([#1613](https://github.com/puppetlabs/beaker/pull/1613))
22
+
23
+ ### Fixed
24
+
25
+ - `host.reboot` uses `uptime` rather than `ping` to check host status ([#1619](https://github.com/puppetlabs/beaker/pull/1619))
15
26
 
16
27
  # [4.15.0](https://github.com/puppetlabs/beaker/compare/4.14.1...4.15.0) - 2020-01-30
17
28
 
data/README.md CHANGED
@@ -40,4 +40,13 @@ If you'd like to contribute improvements to Beaker, please see [CONTRIBUTING](CO
40
40
 
41
41
  # Maintainers
42
42
 
43
- For information on project maintainers, please check out our [MAINTAINERS doc](MAINTAINERS.md).
43
+ For information on project maintainers, please check out our [CODEOWNERS doc](CODEOWNERS).
44
+
45
+ # Releasing
46
+
47
+ Since the beaker project has always been the central hub for all beaker testing, its release process has been more involved than most beaker-libraries (ie. [beaker-puppet](https://github.com/puppetlabs/beaker-puppet)).
48
+ Historically, the process has been described in [Confluence: Beaker Release Process](https://confluence.puppetlabs.com/display/SRE/Beaker+Release+Process) (apologies, most links in this section are Puppet-internal).
49
+
50
+ To release new versions of beaker, please use this [jenkins job](https://jenkins-sre.delivery.puppetlabs.net/view/all/job/qe_beaker-gem_init-multijob_master/). This job lives on internal infrastructure.
51
+
52
+ To run the job, click on `Build with Parameters` in the menu on the left. Make sure you verify the checkbox next to `PUBLIC` is checked and enter the appropriate version. The version should adhere to semantic version standards. When in doubt, consult [the maintainers of Beaker](#maintainers) for guidance.
@@ -45,7 +45,6 @@ Gem::Specification.new do |s|
45
45
  s.add_runtime_dependency 'hocon', '~> 1.0'
46
46
  s.add_runtime_dependency 'net-ssh', '~> 5.0'
47
47
  s.add_runtime_dependency 'net-scp', '~> 1.2'
48
- s.add_runtime_dependency 'net-ping', '~> 2.0'
49
48
  s.add_runtime_dependency 'inifile', '~> 3.0'
50
49
 
51
50
  s.add_runtime_dependency 'rsync', '~> 1.0.9'
@@ -18,15 +18,19 @@ module Beaker
18
18
  #@example
19
19
  # extension = link_exists?("#{URL}.tar.gz") ? ".tar.gz" : ".tar"
20
20
  def link_exists?(link)
21
- require "net/http"
22
- require "net/https"
23
- require "open-uri"
24
- url = URI.parse(link)
25
- http = Net::HTTP.new(url.host, url.port)
26
- http.use_ssl = (url.scheme == 'https')
27
- http.verify_mode = (OpenSSL::SSL::VERIFY_NONE)
28
- http.start do |http|
29
- return http.head(url.request_uri).code == "200"
21
+ begin
22
+ require "net/http"
23
+ require "net/https"
24
+ require "open-uri"
25
+ url = URI.parse(link)
26
+ http = Net::HTTP.new(url.host, url.port)
27
+ http.use_ssl = (url.scheme == 'https')
28
+ http.verify_mode = (OpenSSL::SSL::VERIFY_NONE)
29
+ http.start do |http|
30
+ return http.head(url.request_uri).code == "200"
31
+ end
32
+ rescue
33
+ return false
30
34
  end
31
35
  end
32
36
 
@@ -2,7 +2,6 @@ require 'socket'
2
2
  require 'timeout'
3
3
  require 'benchmark'
4
4
  require 'rsync'
5
- require 'net/ping'
6
5
 
7
6
  require 'beaker/dsl/helpers'
8
7
  require 'beaker/dsl/patterns'
@@ -594,27 +593,6 @@ module Beaker
594
593
  return result if result.success?
595
594
  raise Beaker::Host::CommandFailure, result.error
596
595
  end
597
-
598
- def ping?
599
- check = Net::Ping::External.new(self)
600
- check.ping?
601
- end
602
-
603
- def down?
604
- @logger.debug("host.down?: checking if host has gone down using ping...")
605
- host_up = true
606
- # give it max 3 minutes to go down, check every 10 seconds
607
- repeat_for_and_wait 180, 10 do
608
- host_up = self.ping?
609
- @logger.debug("- ping result: #{host_up}. Done checking? #{!host_up}")
610
- !host_up # host down? -> continue looping. up? -> finished
611
- end
612
- if host_up
613
- raise Beaker::Host::RebootFailure, 'Host failed to go down'
614
- end
615
- @logger.debug("host.down? host stopped responding, returning true")
616
- true
617
- end
618
596
  end
619
597
 
620
598
  [
@@ -1,22 +1,75 @@
1
1
  module Unix::Exec
2
2
  include Beaker::CommandFactory
3
3
 
4
+ # Reboots the host, comparing uptime values to verify success
5
+ #
6
+ # Will throw an exception RebootFailure if it fails
4
7
  def reboot
5
- if self['platform'] =~ /solaris/
6
- exec(Beaker::Command.new("reboot"), :expect_connection_failure => true)
7
- else
8
- exec(Beaker::Command.new("/sbin/shutdown -r now"), :expect_connection_failure => true)
9
- end
10
- down? # Verify the host went down
11
8
  begin
12
- exec(Beaker::Command.new("exit"))
9
+ original_uptime = exec(Beaker::Command.new("uptime"))
10
+ original_uptime_str = parse_uptime original_uptime.stdout
11
+ original_uptime_int = uptime_int original_uptime_str
12
+
13
+ if self['platform'] =~ /solaris/
14
+ exec(Beaker::Command.new("reboot"), :expect_connection_failure => true)
15
+ else
16
+ exec(Beaker::Command.new("/sbin/shutdown -r now"), :expect_connection_failure => true)
17
+ end
18
+
19
+ # give the host a little time to shutdown
20
+ sleep 5
21
+
22
+ #use uptime to check if the host has rebooted
23
+ current_uptime_exec = exec(Beaker::Command.new("uptime"), {:max_connection_tries => 9, :silent => true})
24
+ current_uptime = current_uptime_exec.stdout
25
+ current_uptime_str = parse_uptime current_uptime
26
+ current_uptime_int = uptime_int current_uptime_str
27
+ unless original_uptime_int > current_uptime_int
28
+ raise Beaker::Host::RebootFailure, "Uptime did not reset. Reboot appears to have failed."
29
+ end
13
30
  rescue Beaker::Host::CommandFailure => e
14
31
  raise Beaker::Host::RebootFailure, "Command failed in reboot: #{e.message}"
15
- rescue Exception => e
32
+ rescue RuntimeError => e
16
33
  raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
17
34
  end
18
35
  end
19
36
 
37
+ def uptime_int(uptime_str)
38
+ time_array = uptime_str.split(", ")
39
+ accumulated_mins = 0
40
+ time_array.each do |time_segment|
41
+ value, unit = time_segment.split
42
+ if unit.nil?
43
+ # 20:47 case: hours & mins
44
+ hours, mins = value.split(":")
45
+ accumulated_mins += (hours.to_i * 60 + mins.to_i)
46
+ elsif unit =~ /day(s)?/
47
+ accumulated_mins += (value.to_i * 1440) # 60 * 24 = 1440
48
+ elsif unit =~ /min(s)?/
49
+ accumulated_mins += value.to_i
50
+ else
51
+ raise ArgumentError, "can't parse uptime segment: #{time_segment}"
52
+ end
53
+ end
54
+
55
+ accumulated_mins
56
+ end
57
+
58
+ def parse_uptime(uptime)
59
+ # get String from up to users
60
+ # eg 19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31
61
+ # 8:03 up 52 days, 20:47, 3 users, load averages: 1.36 1.42 1.40
62
+ # 22:19 up 54 days, 1 min, 4 users, load averages: 2.08 2.06 2.27
63
+ regexp = /.*up (.*)[[:space:]]+[[:digit:]]+ user.*/
64
+ result = uptime.match regexp
65
+ if self['platform'] =~ /solaris-/ && result[1].empty?
66
+ return "0 min"
67
+ end
68
+ raise "Couldn't parse uptime: #{uptime}" if result.nil?
69
+
70
+ result[1].strip.chomp(",")
71
+ end
72
+
20
73
  def echo(msg, abs=true)
21
74
  (abs ? '/bin/echo' : 'echo') + " #{msg}"
22
75
  end
@@ -44,37 +44,59 @@ module Beaker
44
44
  connection
45
45
  end
46
46
 
47
- def connect_block host, user, ssh_opts
47
+ # Setup and return the ssh connection object
48
+ #
49
+ # @note For more information about Net::SSH library, check out these docs:
50
+ # - {https://net-ssh.github.io/net-ssh/ Base Net::SSH docs}
51
+ # - {http://net-ssh.github.io/net-ssh/Net/SSH.html#method-c-start Net::SSH.start method docs}
52
+ # - {https://net-ssh.github.io/net-ssh/Net/SSH/Connection/Session.html Net::SSH::Connection::Session class docs}
53
+ #
54
+ # @param [String] host hostname of the machine to connect to
55
+ # @param [String] user username to login to the host as
56
+ # @param [Hash{Symbol=>String}] ssh_opts Options hash passed directly to Net::SSH.start method
57
+ # @param [Hash{Symbol=>String}] options Options hash to control method conditionals
58
+ # @option options [Integer] :max_connection_tries Limit the number of connection start
59
+ # tries to this number (default: 11)
60
+ # @option options [Boolean] :silent Stops logging attempt failure messages if set to true
61
+ # (default: true)
62
+ #
63
+ # @return [Net::SSH::Connection::Session] session returned from Net::SSH.start method
64
+ def connect_block host, user, ssh_opts, options
48
65
  try = 1
49
66
  last_wait = 2
50
67
  wait = 3
68
+ max_connection_tries = options[:max_connection_tries] || 11
51
69
  begin
52
70
  @logger.debug "Attempting ssh connection to #{host}, user: #{user}, opts: #{ssh_opts}"
53
71
  Net::SSH.start(host, user, ssh_opts)
54
72
  rescue *RETRYABLE_EXCEPTIONS => e
55
- if try <= 11
56
- @logger.warn "Try #{try} -- Host #{host} unreachable: #{e.class.name} - #{e.message}"
57
- @logger.warn "Trying again in #{wait} seconds"
73
+ if try <= max_connection_tries
74
+ @logger.warn "Try #{try} -- Host #{host} unreachable: #{e.class.name} - #{e.message}" unless options[:silent]
75
+ @logger.warn "Trying again in #{wait} seconds" unless options[:silent]
58
76
  sleep wait
59
77
  (last_wait, wait) = wait, last_wait + wait
60
78
  try += 1
61
79
  retry
62
80
  else
63
- @logger.warn "Failed to connect to #{host}, after #{try} attempts"
81
+ @logger.warn "Failed to connect to #{host}, after #{try} attempts" unless options[:silent]
64
82
  nil
65
83
  end
66
84
  end
67
85
  end
68
86
 
69
- # connect to the host
70
- def connect
87
+ # Connect to the host, creating a new connection if required
88
+ #
89
+ # @param [Hash{Symbol=>String}] options Options hash to control method conditionals
90
+ # @option options [Integer] :max_connection_tries {#connect_block} option
91
+ # @option options [Boolean] :silent {#connect_block} option
92
+ def connect options = {}
71
93
  # Try three ways to connect to host (vmhostname, ip, hostname)
72
94
  # Try each method in turn until we succeed
73
95
  methods = @ssh_connection_preference.dup
74
96
  while (not @ssh) && (not methods.empty?) do
75
97
  unless instance_variable_get("@#{methods[0]}").nil?
76
98
  if SUPPORTED_CONNECTION_METHODS.include?(methods[0])
77
- @ssh = connect_block(instance_variable_get("@#{methods[0].to_s}"), @user, @ssh_opts)
99
+ @ssh = connect_block(instance_variable_get("@#{methods[0].to_s}"), @user, @ssh_opts, options)
78
100
  else
79
101
  @logger.warn "Beaker does not support #{methods[0]} to SSH to host, trying next available method."
80
102
  @ssh_connection_preference.delete(methods[0])
@@ -196,10 +218,15 @@ module Beaker
196
218
  result
197
219
  end
198
220
 
221
+ # Execute a command on a host, ensuring a connection exists first
222
+ #
223
+ # @param [Hash{Symbol=>String}] options Options hash to control method conditionals
224
+ # @option options [Integer] :max_connection_tries {#connect_block} option (passed through {#connect})
225
+ # @option options [Boolean] :silent {#connect_block} option (passed through {#connect})
199
226
  def execute command, options = {}, stdout_callback = nil,
200
227
  stderr_callback = stdout_callback
201
228
  # ensure that we have a current connection object
202
- connect
229
+ connect(options)
203
230
  try_to_execute(command, options, stdout_callback, stderr_callback)
204
231
  end
205
232
 
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '4.15.0'
3
+ STRING = '4.16.0'
4
4
  end
5
5
  end
@@ -159,19 +159,36 @@ module Beaker
159
159
  end
160
160
 
161
161
  describe '#reboot' do
162
+ let (:response) { double( 'response' ) }
163
+
162
164
  before :each do
163
- expect(instance).to receive(:exec).and_return(false)
164
- expect(instance).to receive(:down?)
165
+ # stubs enough to survive the first uptime call & output parsing
166
+ # note: just stubs input-chain between calls, parsing methods still run
167
+ allow( Beaker::Command ).to receive(:new).with("uptime").and_return(:uptime_command_stub)
168
+ allow( instance ).to receive( :exec ).with(:uptime_command_stub).and_return(response)
169
+ allow( response ).to receive(:stdout).and_return('19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31')
170
+
171
+ allow( Beaker::Command ).to receive(:new).with("/sbin/shutdown -r now").and_return(:shutdown_command_stub)
165
172
  end
173
+
166
174
  it 'raises a reboot failure when command fails' do
167
- expect(instance).to receive(:exec).and_raise(Host::CommandFailure)
175
+ expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Host::CommandFailure)
168
176
  expect{ instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Command failed in reboot: .*/)
169
177
  end
170
178
 
171
179
  it 'raises a reboot failure when we receive an unexpected error' do
172
- expect(instance).to receive(:exec).and_raise(Net::SSH::HostKeyError)
180
+ expect(instance).to receive(:exec).with(:shutdown_command_stub, anything).and_raise(Net::SSH::HostKeyError)
173
181
  expect{ instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Unexpected exception in reboot: .*/)
174
182
  end
183
+
184
+ it 'raises RebootFailure if new uptime is never less than old uptime' do
185
+ # bypass shutdown command itself
186
+ allow( instance ).to receive( :exec ).with(:shutdown_command_stub, anything).and_return(response)
187
+ # allow the second uptime and the hash arguments in exec
188
+ allow( instance ).to receive( :exec ).with(:uptime_command_stub, anything).and_return(response)
189
+
190
+ expect { instance.reboot }.to raise_error(Beaker::Host::RebootFailure, /Uptime did not reset/)
191
+ end
175
192
  end
176
193
 
177
194
  describe '#enable_remote_rsyslog' do
@@ -184,5 +201,42 @@ module Beaker
184
201
  end
185
202
 
186
203
  end
204
+
205
+ describe '#parse_uptime' do
206
+ it 'parses variation of uptime string' do
207
+ expect(instance.parse_uptime("19:52 up 14 mins, 2 users, load averages: 2.95 4.19 4.31")).to be == "14 mins"
208
+ end
209
+ it 'parses variation 2 of uptime string' do
210
+ 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"
211
+ end
212
+ it 'parses variation 3 of uptime string' do
213
+ 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"
214
+ end
215
+ it 'parses variation 4 of uptime string' do
216
+ expect(instance.parse_uptime("18:44:45 up 5 min, 0 users, load average: 0.14, 0.11, 0.05")).to be == "5 min"
217
+ end
218
+ it 'parses solaris\'s "just up" without time message' do
219
+ opts['platform'] = 'solaris-11-x86_64'
220
+ expect(instance.parse_uptime("10:05am up 0 users, load average: 0.66, 0.14, 0.05")).to be == "0 min"
221
+ end
222
+ end
223
+
224
+ describe '#uptime_int' do
225
+ it 'parses time segment variation into a minute value' do
226
+ expect(instance.uptime_int("14 mins")).to be == 14
227
+ end
228
+ it 'parses time segment variation 2 into a minute value' do
229
+ expect(instance.uptime_int("52 days, 20:47")).to be == 76127
230
+ end
231
+ it 'parses time segment variation 3 into a minute value' do
232
+ expect(instance.uptime_int("54 days, 1 min")).to be == 77761
233
+ end
234
+ it 'parses time segment variation 4 into a minute value' do
235
+ expect(instance.uptime_int("54 days")).to be == 77760
236
+ end
237
+ it 'raises if we pass garbage to it' do
238
+ expect { instance.uptime_int("solaris roxx my soxx") }.to raise_error
239
+ end
240
+ end
187
241
  end
188
242
  end
@@ -825,30 +825,6 @@ module Beaker
825
825
  end
826
826
  end
827
827
 
828
- describe "#down?" do
829
-
830
- it "repeats & fails with 'failed to go down' after X seconds" do
831
- allow(host).to receive(:repeat_for_and_wait).with(180,10).and_return(false)
832
- expect {
833
- host.down?
834
- }.to raise_error(Beaker::Host::RebootFailure, "Host failed to go down")
835
- end
836
-
837
- it "returns that the host is down (true) if ping? is false" do
838
- expect(host).to receive(:ping?).exactly(1).times.and_return(false)
839
-
840
- expect(host.down?).to be true
841
- end
842
-
843
- it "cuts off execution correctly if host becomes unreachable" do
844
- expect(host).to receive(:sleep).exactly(3).times
845
- expect(host).to receive(:ping?).exactly(3).times.and_return(true).ordered
846
- expect(host).to receive(:ping?).exactly(1).times.and_return(false).ordered
847
-
848
- expect(host.down?).to be true
849
- end
850
- end
851
-
852
828
  describe "#fips_mode?" do
853
829
  it 'returns false on non-el7 hosts' do
854
830
  @platform = 'windows'
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.15.0
4
+ version: 4.16.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-01-30 00:00:00.000000000 Z
11
+ date: 2020-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -240,20 +240,6 @@ dependencies:
240
240
  - - "~>"
241
241
  - !ruby/object:Gem::Version
242
242
  version: '1.2'
243
- - !ruby/object:Gem::Dependency
244
- name: net-ping
245
- requirement: !ruby/object:Gem::Requirement
246
- requirements:
247
- - - "~>"
248
- - !ruby/object:Gem::Version
249
- version: '2.0'
250
- type: :runtime
251
- prerelease: false
252
- version_requirements: !ruby/object:Gem::Requirement
253
- requirements:
254
- - - "~>"
255
- - !ruby/object:Gem::Version
256
- version: '2.0'
257
243
  - !ruby/object:Gem::Dependency
258
244
  name: inifile
259
245
  requirement: !ruby/object:Gem::Requirement