beaker 4.26.0 → 4.29.0

Sign up to get free protection for your applications and to get access to all the features.
data/CODEOWNERS CHANGED
@@ -1,2 +0,0 @@
1
- * @puppetlabs/beaker
2
- * @puppetlabs/dio
data/LICENSE CHANGED
@@ -1,8 +1,194 @@
1
- beaker - Puppet's system level acceptance test harness
2
-
3
1
  Copyright (C) 2011-2015 Puppet Labs Inc
4
2
 
5
- Puppet Labs can be contacted at: info@puppetlabs.com
3
+ Apache License
4
+ Version 2.0, January 2004
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction,
12
+ and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by
15
+ the copyright owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all
18
+ other entities that control, are controlled by, or are under common
19
+ control with that entity. For the purposes of this definition,
20
+ "control" means (i) the power, direct or indirect, to cause the
21
+ direction or management of such entity, whether by contract or
22
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
23
+ outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+ "You" (or "Your") shall mean an individual or Legal Entity
26
+ exercising permissions granted by this License.
27
+
28
+ "Source" form shall mean the preferred form for making modifications,
29
+ including but not limited to software source code, documentation
30
+ source, and configuration files.
31
+
32
+ "Object" form shall mean any form resulting from mechanical
33
+ transformation or translation of a Source form, including but
34
+ not limited to compiled object code, generated documentation,
35
+ and conversions to other media types.
36
+
37
+ "Work" shall mean the work of authorship, whether in Source or
38
+ Object form, made available under the License, as indicated by a
39
+ copyright notice that is included in or attached to the work
40
+ (an example is provided in the Appendix below).
41
+
42
+ "Derivative Works" shall mean any work, whether in Source or Object
43
+ form, that is based on (or derived from) the Work and for which the
44
+ editorial revisions, annotations, elaborations, or other modifications
45
+ represent, as a whole, an original work of authorship. For the purposes
46
+ of this License, Derivative Works shall not include works that remain
47
+ separable from, or merely link (or bind by name) to the interfaces of,
48
+ the Work and Derivative Works thereof.
49
+
50
+ "Contribution" shall mean any work of authorship, including
51
+ the original version of the Work and any modifications or additions
52
+ to that Work or Derivative Works thereof, that is intentionally
53
+ submitted to Licensor for inclusion in the Work by the copyright owner
54
+ or by an individual or Legal Entity authorized to submit on behalf of
55
+ the copyright owner. For the purposes of this definition, "submitted"
56
+ means any form of electronic, verbal, or written communication sent
57
+ to the Licensor or its representatives, including but not limited to
58
+ communication on electronic mailing lists, source code control systems,
59
+ and issue tracking systems that are managed by, or on behalf of, the
60
+ Licensor for the purpose of discussing and improving the Work, but
61
+ excluding communication that is conspicuously marked or otherwise
62
+ designated in writing by the copyright owner as "Not a Contribution."
63
+
64
+ "Contributor" shall mean Licensor and any individual or Legal Entity
65
+ on behalf of whom a Contribution has been received by Licensor and
66
+ subsequently incorporated within the Work.
67
+
68
+ 2. Grant of Copyright License. Subject to the terms and conditions of
69
+ this License, each Contributor hereby grants to You a perpetual,
70
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71
+ copyright license to reproduce, prepare Derivative Works of,
72
+ publicly display, publicly perform, sublicense, and distribute the
73
+ Work and such Derivative Works in Source or Object form.
74
+
75
+ 3. Grant of Patent License. Subject to the terms and conditions of
76
+ this License, each Contributor hereby grants to You a perpetual,
77
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78
+ (except as stated in this section) patent license to make, have made,
79
+ use, offer to sell, sell, import, and otherwise transfer the Work,
80
+ where such license applies only to those patent claims licensable
81
+ by such Contributor that are necessarily infringed by their
82
+ Contribution(s) alone or by combination of their Contribution(s)
83
+ with the Work to which such Contribution(s) was submitted. If You
84
+ institute patent litigation against any entity (including a
85
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
86
+ or a Contribution incorporated within the Work constitutes direct
87
+ or contributory patent infringement, then any patent licenses
88
+ granted to You under this License for that Work shall terminate
89
+ as of the date such litigation is filed.
90
+
91
+ 4. Redistribution. You may reproduce and distribute copies of the
92
+ Work or Derivative Works thereof in any medium, with or without
93
+ modifications, and in Source or Object form, provided that You
94
+ meet the following conditions:
95
+
96
+ (a) You must give any other recipients of the Work or
97
+ Derivative Works a copy of this License; and
98
+
99
+ (b) You must cause any modified files to carry prominent notices
100
+ stating that You changed the files; and
101
+
102
+ (c) You must retain, in the Source form of any Derivative Works
103
+ that You distribute, all copyright, patent, trademark, and
104
+ attribution notices from the Source form of the Work,
105
+ excluding those notices that do not pertain to any part of
106
+ the Derivative Works; and
107
+
108
+ (d) If the Work includes a "NOTICE" text file as part of its
109
+ distribution, then any Derivative Works that You distribute must
110
+ include a readable copy of the attribution notices contained
111
+ within such NOTICE file, excluding those notices that do not
112
+ pertain to any part of the Derivative Works, in at least one
113
+ of the following places: within a NOTICE text file distributed
114
+ as part of the Derivative Works; within the Source form or
115
+ documentation, if provided along with the Derivative Works; or,
116
+ within a display generated by the Derivative Works, if and
117
+ wherever such third-party notices normally appear. The contents
118
+ of the NOTICE file are for informational purposes only and
119
+ do not modify the License. You may add Your own attribution
120
+ notices within Derivative Works that You distribute, alongside
121
+ or as an addendum to the NOTICE text from the Work, provided
122
+ that such additional attribution notices cannot be construed
123
+ as modifying the License.
124
+
125
+ You may add Your own copyright statement to Your modifications and
126
+ may provide additional or different license terms and conditions
127
+ for use, reproduction, or distribution of Your modifications, or
128
+ for any such Derivative Works as a whole, provided Your use,
129
+ reproduction, and distribution of the Work otherwise complies with
130
+ the conditions stated in this License.
131
+
132
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
133
+ any Contribution intentionally submitted for inclusion in the Work
134
+ by You to the Licensor shall be under the terms and conditions of
135
+ this License, without any additional terms or conditions.
136
+ Notwithstanding the above, nothing herein shall supersede or modify
137
+ the terms of any separate license agreement you may have executed
138
+ with Licensor regarding such Contributions.
139
+
140
+ 6. Trademarks. This License does not grant permission to use the trade
141
+ names, trademarks, service marks, or product names of the Licensor,
142
+ except as required for reasonable and customary use in describing the
143
+ origin of the Work and reproducing the content of the NOTICE file.
144
+
145
+ 7. Disclaimer of Warranty. Unless required by applicable law or
146
+ agreed to in writing, Licensor provides the Work (and each
147
+ Contributor provides its Contributions) on an "AS IS" BASIS,
148
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+ implied, including, without limitation, any warranties or conditions
150
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+ PARTICULAR PURPOSE. You are solely responsible for determining the
152
+ appropriateness of using or redistributing the Work and assume any
153
+ risks associated with Your exercise of permissions under this License.
154
+
155
+ 8. Limitation of Liability. In no event and under no legal theory,
156
+ whether in tort (including negligence), contract, or otherwise,
157
+ unless required by applicable law (such as deliberate and grossly
158
+ negligent acts) or agreed to in writing, shall any Contributor be
159
+ liable to You for damages, including any direct, indirect, special,
160
+ incidental, or consequential damages of any character arising as a
161
+ result of this License or out of the use or inability to use the
162
+ Work (including but not limited to damages for loss of goodwill,
163
+ work stoppage, computer failure or malfunction, or any and all
164
+ other commercial damages or losses), even if such Contributor
165
+ has been advised of the possibility of such damages.
166
+
167
+ 9. Accepting Warranty or Additional Liability. While redistributing
168
+ the Work or Derivative Works thereof, You may choose to offer,
169
+ and charge a fee for, acceptance of support, warranty, indemnity,
170
+ or other liability obligations and/or rights consistent with this
171
+ License. However, in accepting such obligations, You may act only
172
+ on Your own behalf and on Your sole responsibility, not on behalf
173
+ of any other Contributor, and only if You agree to indemnify,
174
+ defend, and hold each Contributor harmless for any liability
175
+ incurred by, or claims asserted against, such Contributor by reason
176
+ of your accepting any such warranty or additional liability.
177
+
178
+ END OF TERMS AND CONDITIONS
179
+
180
+ APPENDIX: How to apply the Apache License to your work.
181
+
182
+ To apply the Apache License to your work, attach the following
183
+ boilerplate notice, with the fields enclosed by brackets "[]"
184
+ replaced with your own identifying information. (Don't include
185
+ the brackets!) The text should be enclosed in the appropriate
186
+ comment syntax for the file format. We also recommend that a
187
+ file or class name and description of purpose be included on the
188
+ same "printed page" as the copyright notice for easier
189
+ identification within third-party archives.
190
+
191
+ Copyright [yyyy] [name of copyright owner]
6
192
 
7
193
  Licensed under the Apache License, Version 2.0 (the "License");
8
194
  you may not use this file except in compliance with the License.
data/Rakefile CHANGED
@@ -26,11 +26,6 @@ task :history do
26
26
  Rake::Task['history:gen'].invoke
27
27
  end
28
28
 
29
- task :travis do
30
- Rake::Task['yard'].invoke if !Beaker::Shared::Semvar.version_is_less(RUBY_VERSION, '2.0.0')
31
- Rake::Task['spec'].invoke
32
- end
33
-
34
29
  module HarnessOptions
35
30
  defaults = {
36
31
  :tests => ['tests'],
data/beaker.gemspec CHANGED
@@ -7,25 +7,25 @@ Gem::Specification.new do |s|
7
7
  s.name = "beaker"
8
8
  s.version = Beaker::Version::STRING
9
9
  s.authors = ["Puppet"]
10
- s.email = ["delivery@puppet.com"]
11
- s.homepage = "https://github.com/puppetlabs/beaker"
10
+ s.email = ["voxpupuli@groups.io"]
11
+ s.homepage = "https://github.com/voxpupuli/beaker"
12
12
  s.summary = %q{Let's test Puppet!}
13
13
  s.description = %q{Puppet's accceptance testing harness}
14
- s.license = 'Apache2'
14
+ s.license = 'Apache-2.0'
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
18
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
19
  s.require_paths = ["lib"]
20
20
 
21
- s.required_ruby_version = Gem::Requirement.new('>= 2.1.8')
21
+ s.required_ruby_version = Gem::Requirement.new('>= 2.4')
22
22
 
23
23
  # Testing dependencies
24
24
  s.add_development_dependency 'rspec', '~> 3.0'
25
25
  s.add_development_dependency 'rspec-its'
26
- s.add_development_dependency 'fakefs', '~> 0.6', '< 0.14.0'
26
+ s.add_development_dependency 'fakefs', '~> 1.0'
27
27
  s.add_development_dependency 'simplecov'
28
- s.add_development_dependency 'rake', '~> 12.0'
28
+ s.add_development_dependency 'rake', '~> 13.0'
29
29
 
30
30
  # Provisioner dependencies - needed for acceptance tests
31
31
  # TODO: figure out how to remove these
@@ -39,13 +39,14 @@ Gem::Specification.new do |s|
39
39
  # Run time dependencies
40
40
  s.add_runtime_dependency 'minitest', '~> 5.4'
41
41
  s.add_runtime_dependency 'minitar', '~> 0.6'
42
- s.add_runtime_dependency 'pry-byebug', '~> 3.6'
42
+ s.add_runtime_dependency 'pry-byebug', '~> 3.9'
43
43
  # pry-byebug can have issues with native readline libs so add rb-readline
44
44
  s.add_runtime_dependency 'rb-readline', '~> 0.5.3'
45
+ s.add_runtime_dependency 'rexml'
45
46
 
46
47
  s.add_runtime_dependency 'hocon', '~> 1.0'
47
48
  s.add_runtime_dependency 'net-ssh', '>= 5.0'
48
- s.add_runtime_dependency 'net-scp', '~> 1.2'
49
+ s.add_runtime_dependency 'net-scp', '>= 1.2', '< 4.0'
49
50
  s.add_runtime_dependency 'inifile', '~> 3.0'
50
51
 
51
52
  s.add_runtime_dependency 'rsync', '~> 1.0.9'
@@ -30,7 +30,10 @@ HOSTS:
30
30
  hypervisor: vmpooler
31
31
  platform: ubuntu-16.04-amd64
32
32
  template: ubuntu-1604-x86_64
33
- ssh_preference: [:vmhostname, :hostname, :ip]
33
+ ssh_preference:
34
+ - :vmhostname
35
+ - :hostname
36
+ - :ip
34
37
  roles:
35
38
  - agent
36
39
  - default
@@ -107,12 +107,11 @@ module Beaker
107
107
  logger.notify " and saving to #{dst}"
108
108
  logger.notify " using command: #{wget_command}"
109
109
 
110
- #in ruby 1.9+ we can upgrade this to popen3 to gain access to the subprocess pid
111
- result = `#{wget_command} 2>&1`
112
- result.each_line do |line|
110
+ stdout_and_stderr_str, status = Open3.capture2e(wget_command)
111
+ stdout_and_stderr_str.each_line do |line|
113
112
  logger.debug(line)
114
113
  end
115
- if $?.to_i != 0
114
+ unless status.success?
116
115
  raise "Failed to fetch_remote_dir '#{url}' (exit code #{$?})"
117
116
  end
118
117
  dst
data/lib/beaker/host.rb CHANGED
@@ -19,6 +19,7 @@ module Beaker
19
19
 
20
20
  class CommandFailure < StandardError; end
21
21
  class RebootFailure < CommandFailure; end
22
+ class RebootWarning < StandardError; end
22
23
 
23
24
  # This class provides array syntax for using puppet --configprint on a host
24
25
  class PuppetConfigReader
@@ -22,7 +22,7 @@ module PSWindows::Exec
22
22
  def rm_rf path
23
23
  # ensure that we have the right slashes for windows
24
24
  path = path.gsub(/\//, '\\')
25
- execute("del /s /q #{path}")
25
+ execute(%(del /s /q "#{path}"))
26
26
  end
27
27
 
28
28
  # Move the origin to destination. The destination is removed prior to moving.
@@ -16,66 +16,99 @@ module Unix::Exec
16
16
 
17
17
  attempts = 0
18
18
 
19
+ # Some systems don't support 'last -F reboot' but it has second granularity
20
+ boot_time_cmd = 'last -F reboot || who -b'
21
+
22
+ # Try to match all of the common formats for 'last' and 'who'
23
+ current_year = Time.now.strftime("%Y")
24
+ boot_time_regex = Regexp.new(%{((?:#{(Date::ABBR_DAYNAMES + Date::ABBR_MONTHNAMES).compact.join('|')}|#{current_year}).+?(\\d+:\\d+)+?(?::(\\d+).+?#{current_year})?)})
25
+
19
26
  original_boot_time_str = nil
20
27
  original_boot_time_line = nil
21
28
  begin
22
- original_boot_time_str = exec(Beaker::Command.new('who -b'), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
29
+ attempts += 1
30
+ # Number of seconds to sleep before rebooting.
31
+ reboot_sleep = 1
32
+
33
+ original_boot_time_str = exec(Beaker::Command.new(boot_time_cmd), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
23
34
  original_boot_time_line = original_boot_time_str.lines.grep(/boot/).first
24
35
 
25
- raise Beaker::Host::RebootFailure, "Could not find system boot time using 'who -b': '#{original_boot_time_str}'" unless original_boot_time_line
36
+ raise Beaker::Host::RebootWarning, "Could not find system boot time using '#{boot_time_cmd}': '#{original_boot_time_str}'" unless original_boot_time_line
26
37
 
27
- original_boot_time = Time.parse(original_boot_time_line)
38
+ original_boot_time_matches = original_boot_time_line.scan(boot_time_regex).last
28
39
 
29
- exec(Beaker::Command.new('/bin/systemctl reboot -i || reboot || /sbin/shutdown -r now'), :expect_connection_failure => true)
30
- rescue Beaker::Host::RebootFailure => e
31
- attempts += 1
32
- if attempts < uptime_retries
33
- @logger.debug("Could not get initial boot time. Will retry #{uptime_retries - attempts} more times.")
34
- retry
35
- else
36
- raise
40
+ raise Beaker::Host::RebootWarning, "Found no valid times in '#{original_boot_time_line}'" unless original_boot_time_matches
41
+
42
+ original_boot_time = Time.parse(original_boot_time_matches.first)
43
+
44
+ unless original_boot_time_matches.last
45
+ reboot_sleep = (61 - Time.now.strftime("%S").to_i)
37
46
  end
38
- rescue Beaker::Host::CommandFailure => e
39
- raise Beaker::Host::RebootFailure, "Command failed when attempting to reboot: #{e.message}"
40
- rescue RuntimeError => e
41
- raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
47
+
48
+ @logger.notify("Sleeping #{reboot_sleep} seconds before rebooting")
49
+
50
+ sleep(reboot_sleep)
51
+
52
+ exec(Beaker::Command.new('/bin/systemctl reboot -i || reboot || /sbin/shutdown -r now'), :expect_connection_failure => true)
42
53
  rescue ArgumentError => e
43
54
  raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
55
+ rescue Beaker::Host::RebootWarning => e
56
+ raise if attempts > uptime_retries
57
+ @logger.warn(e.message)
58
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
59
+ retry
60
+ rescue StandardError => e
61
+ raise if attempts > uptime_retries
62
+ @logger.warn("Unexpected Exception: #{e.message}")
63
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
64
+ @logger.warn(e.backtrace[0,3].join("\n"))
65
+ @logger.debug(e.backtrace.join("\n"))
66
+ retry
44
67
  end
45
68
 
46
69
  attempts = 0
47
70
  begin
71
+ attempts += 1
72
+
48
73
  # give the host a little time to shutdown
49
74
  @logger.debug("Waiting #{wait_time} for host to shut down.")
50
75
  sleep wait_time
51
76
 
52
- current_boot_time_str = exec(Beaker::Command.new('who -b'), {:max_connection_tries => max_connection_tries, :silent => true}).stdout
77
+ # Accept all exit codes because this may fail due to the parallel nature of systemd
78
+ current_boot_time_str = exec(Beaker::Command.new(boot_time_cmd), {:max_connection_tries => max_connection_tries, :silent => true, :accept_all_exit_codes => true}).stdout
53
79
  current_boot_time_line = current_boot_time_str.lines.grep(/boot/).first
54
80
 
55
- raise Beaker::Host::RebootFailure, "Could not find system boot time using 'who -b': '#{current_boot_time_str}'" unless current_boot_time_line
81
+ raise Beaker::Host::RebootWarning, "Could not find system boot time using '#{boot_time_cmd}': '#{current_boot_time_str}'" unless current_boot_time_line
82
+
83
+ current_boot_time_matches = current_boot_time_line.scan(boot_time_regex).last
56
84
 
57
- current_boot_time = Time.parse(current_boot_time_line)
85
+ raise Beaker::Host::RebootWarning, "Found no valid times in '#{current_boot_time_line}'" unless current_boot_time_matches
86
+
87
+ current_boot_time = Time.parse(current_boot_time_matches.first)
58
88
 
59
89
  @logger.debug("Original Boot Time: #{original_boot_time}")
60
90
  @logger.debug("Current Boot Time: #{current_boot_time}")
61
91
 
62
- unless current_boot_time > original_boot_time
92
+ # If this is *exactly* the same then there is really no good way to detect a reboot
93
+ if current_boot_time == original_boot_time
63
94
  raise Beaker::Host::RebootFailure, "Boot time did not reset. Reboot appears to have failed."
64
95
  end
65
- rescue Beaker::Host::RebootFailure => e
66
- attempts += 1
67
- if attempts < uptime_retries
68
- @logger.debug("Boot time did not reset. Will retry #{uptime_retries - attempts} more times.")
69
- retry
70
- else
71
- raise
72
- end
73
- rescue Beaker::Host::CommandFailure => e
74
- raise Beaker::Host::RebootFailure, "Command failed when attempting to reboot: #{e.message}"
75
- rescue RuntimeError => e
76
- raise Beaker::Host::RebootFailure, "Unexpected exception in reboot: #{e.message}"
77
96
  rescue ArgumentError => e
78
97
  raise Beaker::Host::RebootFailure, "Unable to parse time: #{e.message}"
98
+ rescue Beaker::Host::RebootFailure => e
99
+ raise
100
+ rescue Beaker::Host::RebootWarning => e
101
+ raise if attempts > uptime_retries
102
+ @logger.warn(e.message)
103
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
104
+ retry
105
+ rescue StandardError => e
106
+ raise if attempts > uptime_retries
107
+ @logger.warn("Unexpected Exception: #{e.message}")
108
+ @logger.warn("Retrying #{uptime_retries - attempts} more times.")
109
+ @logger.warn(e.backtrace[0,3].join("\n"))
110
+ @logger.debug(e.backtrace.join("\n"))
111
+ retry
79
112
  end
80
113
  end
81
114
 
@@ -423,9 +456,19 @@ module Unix::Exec
423
456
  #@example
424
457
  # host.which('ruby')
425
458
  def which(command)
426
- which_command = "which #{command}"
459
+ unless @which_command
460
+ if execute('type -P true', :accept_all_exit_codes => true).empty?
461
+ if execute('which true', :accept_all_exit_codes => true).empty?
462
+ raise ArgumentError, "Could not find suitable 'which' command"
463
+ else
464
+ @which_command = 'which'
465
+ end
466
+ else
467
+ @which_command = 'type -P'
468
+ end
469
+ end
427
470
 
428
- result = execute(which_command, :accept_all_exit_codes => true)
471
+ result = execute("#{@which_command} #{command}", :accept_all_exit_codes => true)
429
472
  return '' if result.empty?
430
473
 
431
474
  result