beaker 4.15.0 → 4.16.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: 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