beaker 2.25.0 → 2.26.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,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MjM3NjhmMGI2NTg3NDQ3ODViNWRjM2Q3ZTg2MTg0ODQ0NGYzMmIzZg==
4
+ YzQ4ODE5YmE3ZTJlMmJiY2UxYjgyYjEzYjM2NzYxYTkwNjYzZWRhNg==
5
5
  data.tar.gz: !binary |-
6
- MDhmOTFkMTVmOTYxN2VlYTEyMzcwZTZlNDYwNTIxM2E1MTk5MTRlNQ==
6
+ YjRiNzY1ZTEyMTdkYWQwYjAxYWVhY2NiMzE1MzM2ODdmMjEzZTk5ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODM4ODhjYzZiYWI3YmUyYWUwOTk0ZWYwYTI2OTQ5MWIyZjRlMzIyNzk1MDAw
10
- ZDA2OTgzZmZjODRlYjM2ZDFkMmVlODc5MzZjOTVlZjNiZmFjZWQ5Y2I5OWU4
11
- MTRiMWExYWIwYWRiYmJhOTQ2ZjIwMDk2YjA3N2Q4NzJjOWIyYjU=
9
+ NDFmOWFiZWMwMjcxOWMwMGQwMzUxMzFiNDU1ZDE0M2E1ZGI4YmI3ZTY3YjZh
10
+ NjdlYzE5Y2QxZTczMDJmNzI3MTEyZDVmNDExMWQwNGZkYzYxYjY5Y2JhYzVj
11
+ MWI2NTE2NzdjYmY2OWI3Y2I3NTFjN2VjMDE1YmY5OTNiOGU3YWE=
12
12
  data.tar.gz: !binary |-
13
- ZWMwN2MxNDIxNDY0YWRkZjYwNzhmMTBmMDdmOWY4NDliNGI4ZmUwYWNkZjUz
14
- YTJlMDgyZjQxZWNhYjJlMzk3OTc0ODc2ZjNiYjhmYjg2ZTQ1YzkzZDhjMDZm
15
- YTdiMjQ0M2VkOTZkM2E1MWUyZTYxOTM2NTI0MWEyNmE4MmNiYWU=
13
+ NjViMjNhMDc1MDVhZTY5MThlNjQyM2M3ZTE2MDdiOTU4ZmQ0N2U1OGIwYWRj
14
+ MWMxYjA2NThhMWM0YzU5YjNmNmQ4MDI4OWY0ZTU2NmEzZmI4ZTAzMDc1OTM4
15
+ NGI0YTQ2NGQ1ODY0M2Q1MzVlMjVjMmM3ZGFkMzBlMzRlM2E3NWI=
data/HISTORY.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # default - History
2
2
  ## Tags
3
- * [LATEST - 1 Oct, 2015 (e21f5581)](#LATEST)
3
+ * [LATEST - 13 Oct, 2015 (d581b613)](#LATEST)
4
+ * [2.25.0 - 1 Oct, 2015 (51d4cb1a)](#2.25.0)
4
5
  * [2.24.0 - 15 Sep, 2015 (c12e9054)](#2.24.0)
5
6
  * [2.23.0 - 9 Sep, 2015 (2532324a)](#2.23.0)
6
7
  * [2.22.0 - 1 Sep, 2015 (96ec20a7)](#2.22.0)
@@ -97,7 +98,146 @@
97
98
  * [pe1.2 - 6 Sep, 2011 (ba3dadd2)](#pe1.2)
98
99
 
99
100
  ## Details
100
- ### <a name = "LATEST">LATEST - 1 Oct, 2015 (e21f5581)
101
+ ### <a name = "LATEST">LATEST - 13 Oct, 2015 (d581b613)
102
+
103
+ * (GEM) update beaker version to 2.26.0 (d581b613)
104
+
105
+ * Merge pull request #971 from sschneid/sysprofile (e2c5f6fc)
106
+
107
+
108
+ ```
109
+ Merge pull request #971 from sschneid/sysprofile
110
+
111
+ (BKR-580) --collect-perf-data enhancements
112
+ ```
113
+ * Merge pull request #980 from johnduarte/patch-sol-pkg-names (4ef4ba07)
114
+
115
+
116
+ ```
117
+ Merge pull request #980 from johnduarte/patch-sol-pkg-names
118
+
119
+ (maint) Fix Solaris 11 package name logic
120
+ ```
121
+ * Merge pull request #975 from anodelman/win-fix (6a1e14a9)
122
+
123
+
124
+ ```
125
+ Merge pull request #975 from anodelman/win-fix
126
+
127
+ (BKR-275) PowerShell Wrapper Does not Handle Quoting
128
+ ```
129
+ * (BKR-275) PowerShell Wrapper Does not Handle Quoting (5bffbaa0)
130
+
131
+
132
+ ```
133
+ (BKR-275) PowerShell Wrapper Does not Handle Quoting
134
+
135
+ - add spec test coverage for EncodedCommand support in powershell
136
+ wrapper
137
+ ```
138
+ * (maint) Fix Solaris 11 package name logic (fcbd6018)
139
+
140
+
141
+ ```
142
+ (maint) Fix Solaris 11 package name logic
143
+
144
+ This commit fixes a bug in the Solaris 11 package naming logic
145
+ that dropped the SHA suffix entirely from the final package name.
146
+ The SHA should be incorporated into the package name.
147
+
148
+ An additional spec test has been added for the expected package
149
+ name in this circumstance.
150
+ ```
151
+ * (BKR-275) PowerShell Wrapper Does not Handle Quoting (d364123b)
152
+
153
+
154
+ ```
155
+ (BKR-275) PowerShell Wrapper Does not Handle Quoting
156
+
157
+ - add unicode support
158
+ ```
159
+ * Merge pull request #968 from anodelman/new-platform (ad037a82)
160
+
161
+
162
+ ```
163
+ Merge pull request #968 from anodelman/new-platform
164
+
165
+ (BKR-488) Add support for Windows 10 (x86, x64)
166
+ ```
167
+ * (BKR-275) PowerShell Wrapper Does not Handle Quoting (c530b8c5)
168
+
169
+
170
+ ```
171
+ (BKR-275) PowerShell Wrapper Does not Handle Quoting
172
+
173
+ - create new powershell dsl helper:
174
+ execute_powershell_script_on
175
+ * takes as input a string representing a powershell script
176
+ * create a file on the host containing the script
177
+ * executes the script using powershell -File
178
+ - add the ability to execute an encoded powershell string
179
+ powershell("Set Content -path 'fu.txt', -value 'fu'", {'EncodedCommand => true})
180
+ * the command will be Base64 encoded for you
181
+ * bypasses quoting sadness
182
+ ```
183
+ * Merge pull request #907 from hamidnazari/master (a28c8cf1)
184
+
185
+
186
+ ```
187
+ Merge pull request #907 from hamidnazari/master
188
+
189
+ (BKR-427) Support for Docker Container Names and Container Reuse
190
+ ```
191
+ * (BKR-580) Different Debian vs EL crontab differences (a4c78dac)
192
+
193
+ * (BKR-580) Fix spec tests broken in 9b16bef (3ec3fad0)
194
+
195
+ * (BKR-580) --collect-perf-data enhancements (c57d4d44)
196
+
197
+
198
+ ```
199
+ (BKR-580) --collect-perf-data enhancements
200
+
201
+ * allow --collect-perf-data modes:
202
+
203
+ 'aggressive' (poll every minute)
204
+ 'normal' (poll every 10 minutes)
205
+ 'none' (do not collect perf data)
206
+
207
+ If a mode is unspecified, --collect-perf-data will default to 'normal',
208
+ which mimics past behavior.
209
+
210
+ * allow metric exporting to Graphite:
211
+
212
+ Set via the HOSTS file, eg:
213
+
214
+
215
+ `json
216
+ graphite_server: graphite.example.com
217
+ graphite_perf_data: beaker.perf
218
+
219
+ `
220
+ ```
221
+ * (BKR-427) Added support for Docker container names and container reuse (e8a65c67)
222
+
223
+ * (BKR-488) Add support for Windows 10 (x86, x64) (5b2918db)
224
+
225
+
226
+ ```
227
+ (BKR-488) Add support for Windows 10 (x86, x64)
228
+
229
+ - improve the 'wait_for_connection_failure' ssh connection method
230
+ * increase the timeouts
231
+ * send actual data down the pipe, seems to improve the function of
232
+ the test
233
+ * remove 'abort' calls from the code, we can recover and retry
234
+ * added yard docs
235
+ - add /f to the windows reboot call
236
+ * forces closure of any open applications
237
+ ```
238
+ ### <a name = "2.25.0">2.25.0 - 1 Oct, 2015 (51d4cb1a)
239
+
240
+ * (HISTORY) update beaker history for gem release 2.25.0 (51d4cb1a)
101
241
 
102
242
  * (GEM) update beaker version to 2.25.0 (e21f5581)
103
243
 
data/lib/beaker/cli.rb CHANGED
@@ -85,7 +85,9 @@ module Beaker
85
85
  provision
86
86
 
87
87
  # Setup perf monitoring if needed
88
- @perf = Beaker::Perf.new( @hosts, @options ) if @options[:collect_perf_data]
88
+ if @options[:collect_perf_data].to_s =~ /(aggressive)|(normal)/
89
+ @perf = Beaker::Perf.new( @hosts, @options )
90
+ end
89
91
 
90
92
  errored = false
91
93
 
@@ -101,11 +103,13 @@ module Beaker
101
103
  #run post-suite if we are in fail-slow mode
102
104
  if @options[:fail_mode].to_s =~ /slow/
103
105
  run_suite(:post_suite)
106
+ @perf.print_perf_info if defined? @perf
104
107
  end
105
108
  raise e
106
109
  else
107
110
  #post acceptance on success
108
111
  run_suite(:post_suite)
112
+ @perf.print_perf_info if defined? @perf
109
113
  end
110
114
  #cleanup phase
111
115
  rescue => e
@@ -119,7 +123,6 @@ module Beaker
119
123
  preserve_hosts_file
120
124
  end
121
125
 
122
- @perf.print_perf_info if @options[:collect_perf_data]
123
126
  print_reproduction_info( :error )
124
127
 
125
128
  @logger.error "Failed running the test suite."
@@ -139,7 +142,6 @@ module Beaker
139
142
  if @logger.is_debug?
140
143
  print_reproduction_info( :debug )
141
144
  end
142
- @perf.print_perf_info if @options[:collect_perf_data]
143
145
  end
144
146
  end
145
147
 
@@ -265,6 +265,25 @@ module Beaker
265
265
  end
266
266
  end
267
267
 
268
+ # Execute a powershell script from file, remote file created from provided string
269
+ # @note This method uses Tempfile in Ruby's STDLIB as well as {#create_remote_file}.
270
+ #
271
+ # @param [Host] hosts One or more hosts (or some object
272
+ # that responds like
273
+ # {Beaker::Host#do_scp_from}.
274
+ # @param [String] powershell_script A string describing a set of powershell actions
275
+ #
276
+ # @return [Result] Returns the result of the powershell command execution
277
+ def execute_powershell_script_on(hosts, powershell_script, opts = {})
278
+ block_on hosts do |host|
279
+ script_path = "beaker_powershell_script_#{Time.now.to_i}.ps1"
280
+ create_remote_file(host, script_path, powershell_script, opts)
281
+ native_path = script_path.gsub(/\//, "\\")
282
+ on host, powershell("", {"File" => native_path }), opts
283
+ end
284
+
285
+ end
286
+
268
287
  # Move a local script to a remote host and execute it
269
288
  # @note this relies on {#on} and {#scp_to}
270
289
  #
@@ -1182,6 +1182,7 @@ module Beaker
1182
1182
  component_version.gsub!(/(^-)|(-$)/, '')
1183
1183
  # Here we strip leading 0 from version components but leave
1184
1184
  # singular 0 on their own.
1185
+ component_version = component_version.split('-').join('.')
1185
1186
  component_version = component_version.split('.').map(&:to_i).join('.')
1186
1187
  end
1187
1188
  release_file = "puppet-agent#{solaris_name_conjunction}#{component_version}#{solaris_release_version}#{solaris_revision_conjunction}#{revision}.#{arch}.#{pkg_suffix}"
@@ -1,3 +1,4 @@
1
+ require 'base64'
1
2
  module Beaker
2
3
  module DSL
3
4
  # These are wrappers to equivalent {Beaker::Command} objects
@@ -107,7 +108,7 @@ module Beaker
107
108
  # Returns a {Beaker::Command} object for executing powershell commands on a host
108
109
  #
109
110
  # @param [String] command The powershell command to execute
110
- # @param [Hash] args The commandline paramaeters to be passed to powershell
111
+ # @param [Hash] args The commandline parameters to be passed to powershell
111
112
  #
112
113
  # @example Setting the contents of a file
113
114
  # powershell("Set-Content -path 'fu.txt' -value 'fu'")
@@ -115,6 +116,12 @@ module Beaker
115
116
  # @example Using an alternative execution policy
116
117
  # powershell("Set-Content -path 'fu.txt' -value 'fu'", {'ExecutionPolicy' => 'Unrestricted'})
117
118
  #
119
+ # @example Using an EncodedCommand (defaults to non-encoded)
120
+ # powershell("Set Content -path 'fu.txt', -value 'fu'", {'EncodedCommand => true})
121
+ #
122
+ # @example executing from a file
123
+ # powershell("", {'-File' => '/path/to/file'})
124
+ #
118
125
  # @return [Command]
119
126
  def powershell(command, args={})
120
127
  ps_opts = {
@@ -124,8 +131,17 @@ module Beaker
124
131
  'NoProfile' => '',
125
132
  'NonInteractive' => ''
126
133
  }
134
+ encoded = false
127
135
  ps_opts.merge!(args)
128
136
  ps_args = []
137
+
138
+ # determine if the command should be encoded
139
+ if ps_opts.has_key?('EncodedCommand')
140
+ v = ps_opts.delete('EncodedCommand')
141
+ # encode the commend if v is true, nil or empty
142
+ encoded = v || v.eql?('') || v.nil?
143
+ end
144
+
129
145
  ps_opts.each do |k, v|
130
146
  if v.eql?('') or v.nil?
131
147
  ps_args << "-#{k}"
@@ -133,10 +149,31 @@ module Beaker
133
149
  ps_args << "-#{k} #{v}"
134
150
  end
135
151
  end
136
- ps_args << "-Command #{command}"
152
+
153
+ # may not have a command if executing a file
154
+ if command && !command.empty?
155
+ if encoded
156
+ ps_args << "-EncodedCommand #{encode_command(command)}"
157
+ else
158
+ ps_args << "-Command #{command}"
159
+ end
160
+ end
137
161
 
138
162
  Command.new("powershell.exe", ps_args)
139
163
  end
164
+
165
+ # Convert the provided command string to Base64
166
+ # @param [String] cmd The command to convert to Base64
167
+ # @return [String] The converted string
168
+ # @api private
169
+ def encode_command(cmd)
170
+ cmd = cmd.chars.to_a.join("\x00").chomp
171
+ cmd << "\x00" unless cmd[-1].eql? "\x00"
172
+ # use strict_encode because linefeeds are not correctly handled in our model
173
+ cmd = Base64.strict_encode64(cmd).chomp
174
+ cmd
175
+ end
176
+
140
177
  end
141
178
  end
142
179
  end
data/lib/beaker/host.rb CHANGED
@@ -295,7 +295,7 @@ module Beaker
295
295
  if options[:expect_connection_failure] && result.exit_code
296
296
  # should have had a connection failure, but didn't
297
297
  # wait to see if the connection failure will be generation, otherwise raise error
298
- if not connection.wait_for_connection_failure
298
+ if not connection.wait_for_connection_failure(options, output_callback)
299
299
  raise CommandFailure, "Host '#{self}' should have resulted in a connection failure running:\n #{cmdline}\nLast #{@options[:trace_limit]} lines of output were:\n#{result.formatted_output(@options[:trace_limit])}"
300
300
  end
301
301
  end
@@ -129,7 +129,7 @@ module PSWindows::Exec
129
129
  else
130
130
  val = val.split(/\n/)[0] # only take the first result
131
131
  if clean
132
- val.gsub(/#{key}=/,'')
132
+ val.gsub(/#{key}=/i,'')
133
133
  else
134
134
  val
135
135
  end
@@ -2,7 +2,7 @@ module Windows::Exec
2
2
  include Beaker::CommandFactory
3
3
 
4
4
  def reboot
5
- exec(Beaker::Command.new('shutdown /r /t 0 /d p:4:1 /c "Beaker::Host reboot command issued"'), :expect_connection_failure => true)
5
+ exec(Beaker::Command.new('shutdown /f /r /t 0 /d p:4:1 /c "Beaker::Host reboot command issued"'), :expect_connection_failure => true)
6
6
  # rebooting on windows is sloooooow
7
7
  # give it some breathing room before attempting a reconnect
8
8
  sleep(40)
@@ -64,11 +64,28 @@ module Beaker
64
64
  image_name = image.id
65
65
  end
66
66
 
67
- @logger.debug("Creating container from image #{image_name}")
68
- container = ::Docker::Container.create({
67
+ container_opts = {
69
68
  'Image' => image_name,
70
69
  'Hostname' => host.name,
71
- })
70
+ }
71
+
72
+ unless host['docker_container_name'].nil?
73
+ @logger.debug("Looking for an existing container called #{host['docker_container_name']}")
74
+ existing_container = ::Docker::Container.all.select do |container| container.info['Names'].include? "/#{host['docker_container_name']}" end
75
+
76
+ # Prepare to use the existing container or else create it
77
+ if existing_container.any?
78
+ container = existing_container[0]
79
+ else
80
+ container_opts['name'] = host['docker_container_name']
81
+ end
82
+ end
83
+
84
+ # If the specified container exists, then use it rather creating a new one
85
+ if container.nil?
86
+ @logger.debug("Creating container from image #{image_name}")
87
+ container = ::Docker::Container.create(container_opts)
88
+ end
72
89
 
73
90
  @logger.debug("Starting container #{container.id}")
74
91
  container.start({"PublishAllPorts" => true, "Privileged" => true})
data/lib/beaker/logger.rb CHANGED
@@ -248,9 +248,7 @@ module Beaker
248
248
  # @param args[Array<String>] Strings to be reported
249
249
  def perf_output *args
250
250
  return unless is_debug?
251
- strings = strip_colors_from args
252
- string = strings.join
253
- optionally_color MAGENTA, string, false
251
+ optionally_color MAGENTA, *args
254
252
  end
255
253
 
256
254
  # Report a trace message.
@@ -195,8 +195,14 @@ module Beaker
195
195
  @cmd_options[:validate] = bool
196
196
  end
197
197
 
198
- opts.on '--collect-perf-data', 'Use sysstat on linux hosts to collect performance and load data' do
199
- @cmd_options[:collect_perf_data] = true
198
+ opts.on '--collect-perf-data [MODE]',
199
+ 'Collect SUT performance and load data',
200
+ 'Possible values:',
201
+ 'aggressive (poll every minute)',
202
+ 'normal (poll every 10 minutes)',
203
+ 'none (do not collect perf data)',
204
+ '(default: normal)' do |mode|
205
+ @cmd_options[:collect_perf_data] = mode || 'normal'
200
206
  end
201
207
 
202
208
  opts.on('--version', 'Report currently running version of beaker' ) do
@@ -179,7 +179,7 @@ module Beaker
179
179
  :dot_fog => File.join(ENV['HOME'], '.fog'),
180
180
  :ec2_yaml => 'config/image_templates/ec2.yaml',
181
181
  :help => false,
182
- :collect_perf_data => false,
182
+ :collect_perf_data => 'none',
183
183
  :ssh => {
184
184
  :config => false,
185
185
  :paranoid => false,
data/lib/beaker/perf.rb CHANGED
@@ -46,6 +46,14 @@ module Beaker
46
46
  @logger.perf_output("Creating symlink from /etc/sysstat/sysstat.cron to /etc/cron.d")
47
47
  host.exec(Command.new('ln -s /etc/sysstat/sysstat.cron /etc/cron.d'),:acceptable_exit_codes => [0,1])
48
48
  end
49
+ if @options[:collect_perf_data] =~ /aggressive/
50
+ @logger.perf_output("Enabling aggressive sysstat polling")
51
+ if host['platform'] =~ /debian|ubuntu/
52
+ host.exec(Command.new('sed -i s/5-55\\\/10/*/ /etc/cron.d/sysstat'))
53
+ elsif host['platform'] =~ /centos|el|fedora|oracle|redhats|scientific/
54
+ host.exec(Command.new('sed -i s/*\\\/10/*/ /etc/cron.d/sysstat'))
55
+ end
56
+ end
49
57
  if host['platform'] =~ PERF_START_PLATFORMS # SLES doesn't need this step
50
58
  host.exec(Command.new('service sysstat start'))
51
59
  end
@@ -66,10 +74,54 @@ module Beaker
66
74
  def get_perf_data(host, perf_start, perf_end)
67
75
  @logger.perf_output("Getting perf data for host: " + host)
68
76
  if host['platform'] =~ PERF_SUPPORTED_PLATFORMS # All flavours of Linux
69
- host.exec(Command.new("sar -A -s #{perf_start.strftime("%H:%M:%S")} -e #{perf_end.strftime("%H:%M:%S")}"),:acceptable_exit_codes => [0,1,2])
77
+ if not @options[:collect_perf_data] =~ /aggressive/
78
+ host.exec(Command.new("sar -A -s #{perf_start.strftime("%H:%M:%S")} -e #{perf_end.strftime("%H:%M:%S")}"),:acceptable_exit_codes => [0,1,2])
79
+ end
80
+ if (defined? @options[:graphite_server] and not @options[:graphite_server].nil?) and
81
+ (defined? @options[:graphite_perf_data] and not @options[:graphite_perf_data].nil?)
82
+ export_perf_data_to_graphite(host)
83
+ end
70
84
  else
71
85
  @logger.perf_output("Perf (sysstat) not supported on host: " + host)
72
86
  end
73
87
  end
88
+
89
+ # Send performance report numbers to an external Graphite instance
90
+ # @param [Host] host The host we are working with
91
+ # @return [void] The report is sent to the logging output
92
+ def export_perf_data_to_graphite(host)
93
+ @logger.perf_output("Sending data to Graphite server: " + @options[:graphite_server])
94
+
95
+ data = JSON.parse(host.exec(Command.new("sadf -j -- -A"),:silent => true).stdout)
96
+ hostname = host['vmhostname'].split('.')[0]
97
+
98
+ data['sysstat']['hosts'].each do |host|
99
+ host['statistics'].each do |poll|
100
+ timestamp = DateTime.parse(poll['timestamp']['date'] + ' ' + poll['timestamp']['time']).to_time.to_i
101
+
102
+ poll.keys.each do |stat|
103
+ case stat
104
+ when 'cpu-load-all'
105
+ poll[stat].each do |s|
106
+ s.keys.each do |k|
107
+ next if k == 'cpu'
108
+
109
+ socket = TCPSocket.new(@options[:graphite_server], 2003)
110
+ socket.puts "#{@options[:graphite_perf_data]}.#{hostname}.cpu.#{s['cpu']}.#{k} #{s[k]} #{timestamp}"
111
+ socket.close
112
+ end
113
+ end
114
+
115
+ when 'memory'
116
+ poll[stat].keys.each do |s|
117
+ socket = TCPSocket.new(@options[:graphite_server], 2003)
118
+ socket.puts "#{@options[:graphite_perf_data]}.#{hostname}.memory.#{s} #{poll[stat][s]} #{timestamp}"
119
+ socket.close
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
74
126
  end
75
127
  end
@@ -17,8 +17,11 @@ module Beaker
17
17
  Errno::ECONNREFUSED,
18
18
  Errno::ECONNRESET,
19
19
  Errno::ENETUNREACH,
20
+ Net::SSH::Exception,
20
21
  Net::SSH::Disconnect,
21
22
  Net::SSH::AuthenticationFailed,
23
+ Net::SSH::ChannelRequestFailed,
24
+ Net::SSH::ChannelOpenFailed,
22
25
  IOError,
23
26
  ]
24
27
 
@@ -46,7 +49,7 @@ module Beaker
46
49
  @logger.debug "Attempting ssh connection to #{host}, user: #{user}, opts: #{ssh_opts}"
47
50
  Net::SSH.start(host, user, ssh_opts)
48
51
  rescue *RETRYABLE_EXCEPTIONS => e
49
- if try <= 8
52
+ if try <= 11
50
53
  @logger.warn "Try #{try} -- Host #{host} unreachable: #{e.class.name} - #{e.message}"
51
54
  @logger.warn "Trying again in #{wait} seconds"
52
55
  sleep wait
@@ -103,26 +106,52 @@ module Beaker
103
106
  end
104
107
  end
105
108
 
106
- #We expect the connection to close so wait for that to happen
107
- def wait_for_connection_failure
109
+ # Wait for the ssh connection to fail, returns true on connection failure and false otherwise
110
+ # @param [Hash{Symbol=>String}] options Options hash to control method conditionals
111
+ # @option options [Boolean] :pty Should we request a terminal when attempting
112
+ # to send a command over this connection?
113
+ # @option options [String] :stdin Any input to be sent along with the command
114
+ # @param [IO] stdout_callback An IO stream to send connection stdout to, defaults to nil
115
+ # @param [IO] stderr_callback An IO stream to send connection stderr to, defaults to nil
116
+ # @return [Boolean] true if connection failed, false otherwise
117
+ def wait_for_connection_failure options = {}, stdout_callback = nil, stderr_callback = stdout_callback
108
118
  try = 1
109
- last_wait = 0
110
- wait = 1
119
+ last_wait = 2
120
+ wait = 3
121
+ command = 'echo echo' #can be run on all platforms (I'm looking at you, windows)
111
122
  while try < 11
123
+ result = Result.new(@hostname, command)
112
124
  begin
113
- @logger.debug "Waiting for connection failure on #{@hostname} (attempt #{try}, try again in #{wait} second(s))"
125
+ @logger.notify "Waiting for connection failure on #{@hostname} (attempt #{try}, try again in #{wait} second(s))"
126
+ @logger.debug("\n#{@hostname} #{Time.new.strftime('%H:%M:%S')}$ #{command}")
114
127
  @ssh.open_channel do |channel|
115
- channel.exec('') #Just send something down the pipe
116
- end
117
- loop_tries = 0
118
- #loop is actually loop_forver, so let it try 3 times and then quit instead of endless blocking
119
- @ssh.loop { loop_tries += 1 ; loop_tries < 4 }
128
+ request_terminal_for( channel, command ) if options[:pty]
129
+
130
+ channel.exec(command) do |terminal, success|
131
+ raise Net::SSH::Exception.new("FAILED: to execute command on a new channel on #{@hostname}") unless success
132
+ register_stdout_for terminal, result, stdout_callback
133
+ register_stderr_for terminal, result, stderr_callback
134
+ register_exit_code_for terminal, result
135
+
136
+ process_stdin_for( terminal, options[:stdin] ) if options[:stdin]
137
+ end
138
+ end
139
+ loop_tries = 0
140
+ #loop is actually loop_forever, so let it try 3 times and then quit instead of endless blocking
141
+ @ssh.loop { loop_tries += 1 ; loop_tries < 4 }
120
142
  rescue *RETRYABLE_EXCEPTIONS => e
121
143
  @logger.debug "Connection on #{@hostname} failed as expected (#{e.class.name} - #{e.message})"
122
144
  close #this connection is bad, shut it down
123
145
  return true
124
146
  end
125
- sleep wait
147
+ slept = 0
148
+ stdout_callback.call("sleep #{wait} second(s): ")
149
+ while slept < wait
150
+ sleep slept
151
+ stdout_callback.call('.')
152
+ slept += 1
153
+ end
154
+ stdout_callback.call("\n")
126
155
  (last_wait, wait) = wait, last_wait + wait
127
156
  try += 1
128
157
  end
@@ -143,7 +172,7 @@ module Beaker
143
172
  request_terminal_for( channel, command ) if options[:pty]
144
173
 
145
174
  channel.exec(command) do |terminal, success|
146
- abort "FAILED: to execute command on a new channel on #{@hostname}" unless success
175
+ raise Net::SSH::Exception.new("FAILED: to execute command on a new channel on #{@hostname}") unless success
147
176
  register_stdout_for terminal, result, stdout_callback
148
177
  register_stderr_for terminal, result, stderr_callback
149
178
  register_exit_code_for terminal, result
@@ -198,8 +227,8 @@ module Beaker
198
227
  if success
199
228
  @logger.debug "Allocated a PTY on #{@hostname} for #{command.inspect}"
200
229
  else
201
- abort "FAILED: could not allocate a pty when requested on " +
202
- "#{@hostname} for #{command.inspect}"
230
+ raise Net::SSH::Exception.new("FAILED: could not allocate a pty when requested on " +
231
+ "#{@hostname} for #{command.inspect}")
203
232
  end
204
233
  end
205
234
  end
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '2.25.0'
3
+ STRING = '2.26.0'
4
4
  end
5
5
  end
@@ -1124,6 +1124,7 @@ describe ClassMixedWithDSLInstallUtils do
1124
1124
  ['1.0.1.0000786.477', '1.0.1.786.477'],
1125
1125
  ['1.000000.1.786.477', '1.0.1.786.477'],
1126
1126
  ['-1.0.1.786.477', '1.0.1.786.477'],
1127
+ ['1.2.5-78-gbb3022f', '1.2.5.78.3022'],
1127
1128
  ['1.2.5.38.6813', '1.2.5.38.6813']
1128
1129
  ].each do |val, expected|
1129
1130
 
@@ -72,5 +72,54 @@ describe ClassMixedWithDSLWrappers do
72
72
  expect( command.args ).to be === ["-ExecutionPolicy Unrestricted", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command Set-Content -path 'fu.txt' -value 'fu'"]
73
73
  expect( command.options ).to be === {}
74
74
  end
75
+
76
+ it 'should use EncodedCommand when EncodedCommand => true' do
77
+ cmd = "Set-Content -path 'fu.txt' -value 'fu'"
78
+ cmd = subject.encode_command(cmd)
79
+ command = subject.powershell("Set-Content -path 'fu.txt' -value 'fu'", {'EncodedCommand' => true})
80
+ expect(command.command ).to be === 'powershell.exe'
81
+ expect( command.args).to be === ["-ExecutionPolicy Bypass", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand #{cmd}"]
82
+ expect( command.options ).to be === {}
83
+ end
84
+
85
+ it 'should use EncodedCommand when EncodedCommand => ""' do
86
+ cmd = "Set-Content -path 'fu.txt' -value 'fu'"
87
+ cmd = subject.encode_command(cmd)
88
+ command = subject.powershell("Set-Content -path 'fu.txt' -value 'fu'", {'EncodedCommand' => ""})
89
+ expect(command.command ).to be === 'powershell.exe'
90
+ expect( command.args).to be === ["-ExecutionPolicy Bypass", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand #{cmd}"]
91
+ expect( command.options ).to be === {}
92
+ end
93
+
94
+ it 'should use EncodedCommand when EncodedCommand => nil' do
95
+ cmd = "Set-Content -path 'fu.txt' -value 'fu'"
96
+ cmd = subject.encode_command(cmd)
97
+ command = subject.powershell("Set-Content -path 'fu.txt' -value 'fu'", {'EncodedCommand' => nil})
98
+ expect(command.command ).to be === 'powershell.exe'
99
+ expect( command.args).to be === ["-ExecutionPolicy Bypass", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand #{cmd}"]
100
+ expect( command.options ).to be === {}
101
+ end
102
+
103
+ it 'should not use EncodedCommand when EncodedCommand => false' do
104
+ command = subject.powershell("Set-Content -path 'fu.txt' -value 'fu'", {'EncodedCommand' => false})
105
+ expect(command.command ).to be === 'powershell.exe'
106
+ expect( command.args).to be === ["-ExecutionPolicy Bypass", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command Set-Content -path 'fu.txt' -value 'fu'"]
107
+ expect( command.options ).to be === {}
108
+ end
109
+
110
+ it 'should not use EncodedCommand when EncodedCommand not present' do
111
+ command = subject.powershell("Set-Content -path 'fu.txt' -value 'fu'", {})
112
+ expect(command.command ).to be === 'powershell.exe'
113
+ expect( command.args).to be === ["-ExecutionPolicy Bypass", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command Set-Content -path 'fu.txt' -value 'fu'"]
114
+ expect( command.options ).to be === {}
115
+ end
116
+
117
+ it 'has no -Command/-EncodedCommand when command is empty' do
118
+ command = subject.powershell("", {"File" => 'myfile.ps1'})
119
+ expect(command.command ).to be === 'powershell.exe'
120
+ expect( command.args).to be === ["-ExecutionPolicy Bypass", "-InputFormat None", "-NoLogo", "-NoProfile", "-NonInteractive", "-File myfile.ps1"]
121
+ expect( command.options ).to be === {}
122
+
123
+ end
75
124
  end
76
125
  end
@@ -39,6 +39,9 @@ module Beaker
39
39
  container = double('Docker::Container')
40
40
  allow( container ).to receive(:id)
41
41
  allow( container ).to receive(:start)
42
+ allow( container ).to receive(:info).and_return(
43
+ *(0..2).map { |index| { 'Names' => ["/spec-container-#{index}"] } }
44
+ )
42
45
  allow( container ).to receive(:json).and_return({
43
46
  'NetworkSettings' => {
44
47
  'IPAddress' => '192.0.2.1',
@@ -162,6 +165,35 @@ module Beaker
162
165
  docker.provision
163
166
  end
164
167
 
168
+ it 'should create a named container based on the Image (identified by image.id)' do
169
+ hosts.each_with_index do |host, index|
170
+ container_name = "spec-container-#{index}"
171
+ host['docker_container_name'] = container_name
172
+
173
+ expect( ::Docker::Container ).to receive(:all).and_return([])
174
+
175
+ expect( ::Docker::Container ).to receive(:create).with({
176
+ 'Image' => image.id,
177
+ 'Hostname' => host.name,
178
+ 'name' => container_name,
179
+ })
180
+ end
181
+
182
+ docker.provision
183
+ end
184
+
185
+ it 'should not create a container if a named one already exists' do
186
+ hosts.each_with_index do |host, index|
187
+ container_name = "spec-container-#{index}"
188
+ host['docker_container_name'] = container_name
189
+
190
+ expect( ::Docker::Container ).to receive(:all).and_return([container])
191
+ expect( ::Docker::Container ).not_to receive(:create)
192
+ end
193
+
194
+ docker.provision
195
+ end
196
+
165
197
  it 'should start the container' do
166
198
  expect( container ).to receive(:start).with({'PublishAllPorts' => true, 'Privileged' => true})
167
199
 
@@ -7,7 +7,7 @@ module Beaker
7
7
  let(:parser) {Beaker::Options::CommandLineParser.new}
8
8
  let(:test_opts) {["-h", "vcloud.cfg", "--debug", "--tests", "test.rb", "--help"]}
9
9
  let(:full_opts_in) {["--hosts", "host.cfg", "--options", "opts_file", "--helper", "path_to_helper", "--load-path", "load_path", "--tests", "test1.rb,test2.rb,test3.rb", "--pre-suite", "pre_suite.rb", "--post-suite", "post_suite.rb", "--no-provision", "--preserve-hosts", "always", "--root-keys", "--keyfile", "../.ssh/id_rsa", "--install", "gitrepopath", "-m", "module", "-q", "--dry-run", "--no-ntp", "--repo-proxy", "--add-el-extras", "--config", "anotherfile.cfg", "--fail-mode", "fast", "--no-color", "--no-color-host-output", "--version", "--log-level", "info", "--package-proxy", "http://192.168.100.1:3128", "--collect-perf-data", "--parse-only", "--validate", "--timeout", "40", "--log-prefix", "pants", "--configure", "--tag", "1,2,3", "--exclude-tag", "4,5,6", "--xml-time-order"]}
10
- let(:full_opts_out) {{:hosts_file=>"anotherfile.cfg",:options_file=>"opts_file", :helper => "path_to_helper", :load_path => "load_path", :tests => "test1.rb,test2.rb,test3.rb", :pre_suite => "pre_suite.rb", :post_suite => "post_suite.rb", :provision=>false, :preserve_hosts => "always", :root_keys=>true, :keyfile => "../.ssh/id_rsa", :install => "gitrepopath", :modules=>"module", :quiet=>true, :dry_run=>true, :timesync=>false, :repo_proxy=>true, :add_el_extras=>true, :fail_mode => "fast", :color=>false, :color_host_output=>false, :beaker_version_print=>true, :log_level => "info", :package_proxy => "http://192.168.100.1:3128", :collect_perf_data=>true, :parse_only=>true, :validate=>true, :timeout => "40", :log_prefix => "pants", :configure => true, :tag_includes => "1,2,3", :tag_excludes => "4,5,6", :xml_time_enabled => true}}
10
+ let(:full_opts_out) {{:hosts_file=>"anotherfile.cfg",:options_file=>"opts_file", :helper => "path_to_helper", :load_path => "load_path", :tests => "test1.rb,test2.rb,test3.rb", :pre_suite => "pre_suite.rb", :post_suite => "post_suite.rb", :provision=>false, :preserve_hosts => "always", :root_keys=>true, :keyfile => "../.ssh/id_rsa", :install => "gitrepopath", :modules=>"module", :quiet=>true, :dry_run=>true, :timesync=>false, :repo_proxy=>true, :add_el_extras=>true, :fail_mode => "fast", :color=>false, :color_host_output=>false, :beaker_version_print=>true, :log_level => "info", :package_proxy => "http://192.168.100.1:3128", :collect_perf_data=>"normal", :parse_only=>true, :validate=>true, :timeout => "40", :log_prefix => "pants", :configure => true, :tag_includes => "1,2,3", :tag_excludes => "4,5,6", :xml_time_enabled => true}}
11
11
  let(:validate_true) {["--validate"]}
12
12
  let(:validate_false) {["--no-validate"]}
13
13
  let(:configure_true) {['--configure']}
@@ -40,7 +40,7 @@ module Beaker
40
40
  @my_logger.remove_destination(STDOUT)
41
41
  perf = Perf.new( hosts, @options )
42
42
  expect( perf ).to be_a_kind_of Perf
43
- expect(@my_io.string).to match(/Setup perf on host: myHost*Setup perf on host: myOtherHost/)
43
+ expect(@my_io.string).to match(/Setup perf on host: myHost*\nSetup perf on host: myOtherHost/)
44
44
  end
45
45
 
46
46
  it 'creates a new Perf object with multiple hosts, :collect_perf_data = true, SLES' do
@@ -50,7 +50,7 @@ module Beaker
50
50
  @my_logger.remove_destination(STDOUT)
51
51
  perf = Perf.new( hosts, @options )
52
52
  expect( perf ).to be_a_kind_of Perf
53
- expect(@my_io.string).to match(/Setup perf on host: myHostSetup perf on host: myOtherHost/)
53
+ expect(@my_io.string).to match(/Setup perf on host: myHost\nSetup perf on host: myOtherHost/)
54
54
  end
55
55
  end
56
56
 
@@ -73,7 +73,7 @@ module Beaker
73
73
  perf = Perf.new( @hosts, @options )
74
74
  expect( perf ).to be_a_kind_of Perf
75
75
  perf.print_perf_info
76
- expect(@my_io.string).to match(/Setup perf on host: myHostSetup perf on host: myOtherHostPerf \(sysstat\) not supported on host: myOtherHostGetting perf data for host: myHostGetting perf data for host: myOtherHostPerf \(sysstat\) not supported on host: myOtherHost/)
76
+ expect(@my_io.string).to match(/Setup perf on host: myHost\nSetup perf on host: myOtherHost\nPerf \(sysstat\) not supported on host: myOtherHost\nGetting perf data for host: myHost\nGetting perf data for host: myOtherHost\nPerf \(sysstat\) not supported on host: myOtherHost/)
77
77
  end
78
78
 
79
79
  it "Does the Right Thing on non-Linux hosts" do
@@ -82,7 +82,7 @@ module Beaker
82
82
  perf = Perf.new( @hosts, @options )
83
83
  expect( perf ).to be_a_kind_of Perf
84
84
  perf.print_perf_info
85
- expect(@my_io.string).to match(/Setup perf on host: myHostPerf \(sysstat\) not supported on host: myHostSetup perf on host: myOtherHostPerf \(sysstat\) not supported on host: myOtherHostGetting perf data for host: myHostPerf \(sysstat\) not supported on host: myHostGetting perf data for host: myOtherHostPerf \(sysstat\) not supported on host: myOtherHost/)
85
+ expect(@my_io.string).to match(/Setup perf on host: myHost\nPerf \(sysstat\) not supported on host: myHost\nSetup perf on host: myOtherHost\nPerf \(sysstat\) not supported on host: myOtherHost\nGetting perf data for host: myHost\nPerf \(sysstat\) not supported on host: myHost\nGetting perf data for host: myOtherHost\nPerf \(sysstat\) not supported on host: myOtherHost/)
86
86
  end
87
87
  end
88
88
 
@@ -113,7 +113,7 @@ module Beaker
113
113
  end
114
114
 
115
115
  describe '#request_terminal_for' do
116
- it 'fails correctly by calling the abort method' do
116
+ it 'fails correctly by raising Net::SSH::Exception' do
117
117
  mock_ssh = Object.new
118
118
  expect( Net::SSH ).to receive( :start ).with( ip, user, ssh_opts) { mock_ssh }
119
119
  connection.connect
@@ -121,8 +121,7 @@ module Beaker
121
121
  mock_channel = Object.new
122
122
  allow( mock_channel ).to receive( :request_pty ).and_yield(nil, false)
123
123
 
124
- expect( subject ).to receive( :abort ).once
125
- connection.request_terminal_for mock_channel, 'ls'
124
+ expect{ connection.request_terminal_for mock_channel, 'ls' }.to raise_error Net::SSH::Exception
126
125
  end
127
126
  end
128
127
 
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: 2.25.0
4
+ version: 2.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppetlabs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-01 00:00:00.000000000 Z
11
+ date: 2015-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec