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 +8 -8
- data/HISTORY.md +142 -2
- data/lib/beaker/cli.rb +5 -3
- data/lib/beaker/dsl/helpers/host_helpers.rb +19 -0
- data/lib/beaker/dsl/install_utils/foss_utils.rb +1 -0
- data/lib/beaker/dsl/wrappers.rb +39 -2
- data/lib/beaker/host.rb +1 -1
- data/lib/beaker/host/pswindows/exec.rb +1 -1
- data/lib/beaker/host/windows/exec.rb +1 -1
- data/lib/beaker/hypervisor/docker.rb +20 -3
- data/lib/beaker/logger.rb +1 -3
- data/lib/beaker/options/command_line_parser.rb +8 -2
- data/lib/beaker/options/presets.rb +1 -1
- data/lib/beaker/perf.rb +53 -1
- data/lib/beaker/ssh_connection.rb +44 -15
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +1 -0
- data/spec/beaker/dsl/wrappers_spec.rb +49 -0
- data/spec/beaker/hypervisor/docker_spec.rb +32 -0
- data/spec/beaker/options/command_line_parser_spec.rb +1 -1
- data/spec/beaker/perf_spec.rb +4 -4
- data/spec/beaker/ssh_connection_spec.rb +2 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YzQ4ODE5YmE3ZTJlMmJiY2UxYjgyYjEzYjM2NzYxYTkwNjYzZWRhNg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjRiNzY1ZTEyMTdkYWQwYjAxYWVhY2NiMzE1MzM2ODdmMjEzZTk5ZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NDFmOWFiZWMwMjcxOWMwMGQwMzUxMzFiNDU1ZDE0M2E1ZGI4YmI3ZTY3YjZh
|
10
|
+
NjdlYzE5Y2QxZTczMDJmNzI3MTEyZDVmNDExMWQwNGZkYzYxYjY5Y2JhYzVj
|
11
|
+
MWI2NTE2NzdjYmY2OWI3Y2I3NTFjN2VjMDE1YmY5OTNiOGU3YWE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjViMjNhMDc1MDVhZTY5MThlNjQyM2M3ZTE2MDdiOTU4ZmQ0N2U1OGIwYWRj
|
14
|
+
MWMxYjA2NThhMWM0YzU5YjNmNmQ4MDI4OWY0ZTU2NmEzZmI4ZTAzMDc1OTM4
|
15
|
+
NGI0YTQ2NGQ1ODY0M2Q1MzVlMjVjMmM3ZGFkMzBlMzRlM2E3NWI=
|
data/HISTORY.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# default - History
|
2
2
|
## Tags
|
3
|
-
* [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 -
|
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
|
-
|
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}"
|
data/lib/beaker/dsl/wrappers.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
@@ -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
|
-
|
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
|
-
|
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',
|
199
|
-
|
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 =>
|
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
|
-
|
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 <=
|
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
|
-
#
|
107
|
-
|
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 =
|
110
|
-
wait =
|
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.
|
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
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/beaker/version.rb
CHANGED
@@ -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=>
|
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']}
|
data/spec/beaker/perf_spec.rb
CHANGED
@@ -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
|
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:
|
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:
|
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:
|
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
|
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
|
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.
|
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-
|
11
|
+
date: 2015-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|