beaker 2.25.0 → 2.26.0

Sign up to get free protection for your applications and to get access to all the features.
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