beaker 2.8.0 → 2.9.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 +8 -8
- data/.simplecov +8 -8
- data/HISTORY.md +241 -2
- data/Rakefile +4 -0
- data/beaker.gemspec +0 -1
- data/lib/beaker/dsl/install_utils/ezbake_utils.rb +1 -1
- data/lib/beaker/dsl/install_utils/module_utils.rb +17 -15
- data/lib/beaker/dsl/install_utils/puppet_utils.rb +24 -3
- data/lib/beaker/host.rb +20 -2
- data/lib/beaker/host/freebsd.rb +31 -0
- data/lib/beaker/host/unix/pkg.rb +9 -1
- data/lib/beaker/host_prebuilt_steps.rb +6 -3
- data/lib/beaker/hypervisor/docker.rb +6 -1
- data/lib/beaker/hypervisor/vmpooler.rb +1 -0
- data/lib/beaker/network_manager.rb +10 -1
- data/lib/beaker/options/presets.rb +5 -1
- data/lib/beaker/platform.rb +1 -1
- data/lib/beaker/result.rb +1 -1
- data/lib/beaker/ssh_connection.rb +35 -13
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/dsl/install_utils/module_utils_spec.rb +21 -2
- data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +110 -19
- data/spec/beaker/hypervisor/docker_spec.rb +11 -1
- data/spec/beaker/network_manager_spec.rb +32 -2
- data/spec/beaker/ssh_connection_spec.rb +34 -22
- data/spec/spec_helper.rb +1 -0
- metadata +3 -17
- data/history.rb +0 -60
checksums.yaml
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
!binary "U0hBMQ==":
|
|
3
3
|
metadata.gz: !binary |-
|
|
4
|
-
|
|
4
|
+
NTRiYTFlN2E4ZTQyNWUxMWJlZGRmNzNmNTNmN2NhYzk3OTFjODYzYg==
|
|
5
5
|
data.tar.gz: !binary |-
|
|
6
|
-
|
|
6
|
+
YjNkMGZkODlhYmM3Y2Q2ZDA5OGE1OTM2ODQ2MWIxNzkxYmViMDY3ZQ==
|
|
7
7
|
SHA512:
|
|
8
8
|
metadata.gz: !binary |-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
ZDkyODM0NzY1ZGY0MzdiNzViMzEyZTZhMmRiNTAyNTAzMDQzMmU3Y2IwNTM3
|
|
10
|
+
YmQ5OWViYzYzNDUzYWE5OTlmNGNkZjljNzgwMmE4MmQwODZhNTE3ZTQ4MjZl
|
|
11
|
+
ODE3Mzg1MmMxOTFjMmRhMWM5ZmFmNGRjOWExNWU1MGFhYjQ5MjM=
|
|
12
12
|
data.tar.gz: !binary |-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
MDEzZjUxNmZmMmI1MjdiZjcwZjFjNDY3ODA2MDdlYmY3MTQwNGMxZWNjNjcy
|
|
14
|
+
MmM4NzZhMjcxMDljNWE4NDU4MGE3MmQ2MGQxYWFhOWNjMzYzZTk2M2Y2MmU5
|
|
15
|
+
MThkNWE5MTQzYjRmNDg3MWU3MzQyZjA4MjVlYzZhZDZhZDFkZWE=
|
data/.simplecov
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
SimpleCov.configure do
|
|
2
2
|
add_filter 'spec/'
|
|
3
|
+
add_filter 'vendor/'
|
|
3
4
|
add_filter do |file|
|
|
4
5
|
file.lines_of_code < 10
|
|
5
6
|
end
|
|
6
|
-
add_group '
|
|
7
|
-
add_group '
|
|
8
|
-
add_group '
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
add_group 'Hypervisors', '/hypervisor'
|
|
7
|
+
add_group 'Answers', '/answers/'
|
|
8
|
+
add_group 'DSL', '/dsl/'
|
|
9
|
+
add_group 'Host', '/host/'
|
|
10
|
+
add_group 'Hypervisors', '/hypervisor/'
|
|
11
|
+
add_group 'Options', '/options/'
|
|
12
|
+
add_group 'Shared', '/shared/'
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
SimpleCov.start if ENV['
|
|
15
|
+
SimpleCov.start if ENV['BEAKER_COVERAGE']
|
data/HISTORY.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# default - History
|
|
2
2
|
## Tags
|
|
3
|
-
* [LATEST -
|
|
3
|
+
* [LATEST - 9 Apr, 2015 (901c4a94)](#LATEST)
|
|
4
|
+
* [beaker2.8.0 - 26 Mar, 2015 (2d25d06d)](#beaker2.8.0)
|
|
4
5
|
* [beaker2.7.1 - 19 Mar, 2015 (45b2bf10)](#beaker2.7.1)
|
|
5
6
|
* [beaker2.7.0 - 19 Mar, 2015 (38b14ef8)](#beaker2.7.0)
|
|
6
7
|
* [beaker2.6.0 - 12 Mar, 2015 (d4e731ab)](#beaker2.6.0)
|
|
@@ -76,7 +77,245 @@
|
|
|
76
77
|
* [pe1.2 - 6 Sep, 2011 (ba3dadd2)](#pe1.2)
|
|
77
78
|
|
|
78
79
|
## Details
|
|
79
|
-
### <a name = "LATEST">LATEST -
|
|
80
|
+
### <a name = "LATEST">LATEST - 9 Apr, 2015 (901c4a94)
|
|
81
|
+
|
|
82
|
+
* (GEM) update beaker version to 2.9.0 (901c4a94)
|
|
83
|
+
|
|
84
|
+
* Merge pull request #783 from anodelman/maint (62b5ac60)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
Merge pull request #783 from anodelman/maint
|
|
89
|
+
|
|
90
|
+
(BKR-70) Make SshConnection.close more robust...
|
|
91
|
+
```
|
|
92
|
+
* (BKR-70) Make SshConnection.close more robust... (3bb4f7ca)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
(BKR-70) Make SshConnection.close more robust...
|
|
97
|
+
|
|
98
|
+
...(so that on(host, "reboot") works correctly)
|
|
99
|
+
|
|
100
|
+
- catch IOErrors
|
|
101
|
+
- added additional execution option ':expect_connection_failure' for
|
|
102
|
+
operations that you believe should result in the connection to the
|
|
103
|
+
host being dropped or broken
|
|
104
|
+
- tested with rebooting centos7 box, successfully rebuilds connection
|
|
105
|
+
and continues test execution
|
|
106
|
+
```
|
|
107
|
+
* Merge pull request #781 from anodelman/zombie (959952a7)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Merge pull request #781 from anodelman/zombie
|
|
112
|
+
|
|
113
|
+
(BKR-192) can't run beaker without a host file specified
|
|
114
|
+
```
|
|
115
|
+
* (BKR-192) can't run beaker without a host file specified (5cf28b4c)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
(BKR-192) can't run beaker without a host file specified
|
|
120
|
+
|
|
121
|
+
- the hosts file was being defaulted to to provide a log name prefix -
|
|
122
|
+
it needs a default value for when there is no user provided value plus
|
|
123
|
+
no hosts file
|
|
124
|
+
```
|
|
125
|
+
* Merge pull request #778 from anodelman/maint (e2d23066)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
Merge pull request #778 from anodelman/maint
|
|
130
|
+
|
|
131
|
+
(BKR-188) have beaker support a 'test' raketask.
|
|
132
|
+
```
|
|
133
|
+
* Merge pull request #780 from anodelman/spec-tests (a64a3a10)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Merge pull request #780 from anodelman/spec-tests
|
|
138
|
+
|
|
139
|
+
(BKR-183) simplecov busted in beaker
|
|
140
|
+
```
|
|
141
|
+
* (BKR-183) simplecov busted in beaker (3ae9cd7f)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
(BKR-183) simplecov busted in beaker
|
|
146
|
+
|
|
147
|
+
- accidentally busted when we dropped ruby 1.8 support
|
|
148
|
+
- updated to look nice with the current beaker directory structure
|
|
149
|
+
- updated env var to meet beaker standards
|
|
150
|
+
```
|
|
151
|
+
* (BKR-188) have beaker support a 'test' raketask. (93faeffa)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
(BKR-188) have beaker support a 'test' raketask.
|
|
156
|
+
|
|
157
|
+
- meets our gem standards
|
|
158
|
+
```
|
|
159
|
+
* Merge pull request #767 from petems/BKR-113-freebsd_pkg_commands (a18ff029)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
Merge pull request #767 from petems/BKR-113-freebsd_pkg_commands
|
|
164
|
+
|
|
165
|
+
(BKR-113) Add pkg commands for FreeBSD
|
|
166
|
+
```
|
|
167
|
+
* Merge pull request #764 from petems/BKR-113-add_freebsd_host_class (cc6b606e)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
Merge pull request #764 from petems/BKR-113-add_freebsd_host_class
|
|
172
|
+
|
|
173
|
+
(BKR-113) Add FreeBSD host class
|
|
174
|
+
```
|
|
175
|
+
* Merge pull request #768 from anodelman/maint (5a8d8b0c)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Merge pull request #768 from anodelman/maint
|
|
180
|
+
|
|
181
|
+
(QENG-2083) share out beaker's history file generation tool
|
|
182
|
+
```
|
|
183
|
+
* Merge pull request #770 from madAndroid/BKR-165-hack_etc_hosts_docker (bfd12ca9)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
Merge pull request #770 from madAndroid/BKR-165-hack_etc_hosts_docker
|
|
188
|
+
|
|
189
|
+
(BKR-165) hack_etc_hosts method doesn't work for Docker provider
|
|
190
|
+
```
|
|
191
|
+
* Merge pull request #773 from liamjbennett/install_windows_cert (3aa42917)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
Merge pull request #773 from liamjbennett/install_windows_cert
|
|
196
|
+
|
|
197
|
+
(gh-691) Add function to install certs on windows agents
|
|
198
|
+
```
|
|
199
|
+
* Merge pull request #771 from justinstoller/maint/master/pc1 (8909e35a)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
Merge pull request #771 from justinstoller/maint/master/pc1
|
|
204
|
+
|
|
205
|
+
(QENG-2096) Update dev repo for new AIO repo name
|
|
206
|
+
```
|
|
207
|
+
* Merge pull request #762 from sschneid/bkr-161_tagging (084ee947)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
Merge pull request #762 from sschneid/bkr-161_tagging
|
|
212
|
+
|
|
213
|
+
(BKR-161) Beaker tagging improvements
|
|
214
|
+
```
|
|
215
|
+
* (gh-691) Add function to install certs on windows agents (ccf0bb7e)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
(gh-691) Add function to install certs on windows agents
|
|
220
|
+
|
|
221
|
+
This fixes the issue raised in PUP-2365
|
|
222
|
+
```
|
|
223
|
+
* BKR-165 - Add spec test to check for presence of /etc/hosts for docker provider (d5da2ee2)
|
|
224
|
+
|
|
225
|
+
* BKR-165 - Amend hack_etc_hosts to set host file entry to host['vm_ip'], if present; add comments to that affect (9c8874b8)
|
|
226
|
+
|
|
227
|
+
* BKR-165 - add hack_etc_hosts method to beaker hypervisor, and set host['vm_ip'] to correct container IP (b324abe7)
|
|
228
|
+
|
|
229
|
+
* (QENG-2096) Update dev repo for new AIO repo name (06e3da46)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
(QENG-2096) Update dev repo for new AIO repo name
|
|
234
|
+
|
|
235
|
+
Soon AIO builds will only be published to the repo "PC1" (not
|
|
236
|
+
products/devel or main as previously). This allows Beaker to install
|
|
237
|
+
from both new AIO repos or legacy repos.
|
|
238
|
+
```
|
|
239
|
+
* (QENG-2083) share out beaker's history file generation tool (afe0d24c)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
(QENG-2083) share out beaker's history file generation tool
|
|
244
|
+
|
|
245
|
+
- remove the history file generator used exclusively by beaker in favor of the
|
|
246
|
+
new, general use tool
|
|
247
|
+
```
|
|
248
|
+
* Merge pull request #763 from mullr/fix-ezbake-package-version (3e2e1e6d)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
Merge pull request #763 from mullr/fix-ezbake-package-version
|
|
253
|
+
|
|
254
|
+
Fix ezbake version string extraction
|
|
255
|
+
```
|
|
256
|
+
* Merge pull request #753 from petems/MAINT-fix_copy_module_to_on_non_cygwin (30a9ee04)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
Merge pull request #753 from petems/MAINT-fix_copy_module_to_on_non_cygwin
|
|
261
|
+
|
|
262
|
+
(MAINT) Fix copy module to on non-cygwin
|
|
263
|
+
```
|
|
264
|
+
* Merge pull request #761 from petems/MAINT-show_error_from_docker (a6198bb4)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
Merge pull request #761 from petems/MAINT-show_error_from_docker
|
|
269
|
+
|
|
270
|
+
(MAINT) Show error from docker
|
|
271
|
+
```
|
|
272
|
+
* (BKR-113) Add pkg commands for FreeBSD (efb39f8e)
|
|
273
|
+
|
|
274
|
+
* (BKR-113) Add FreeBSD host class (e288f713)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
(BKR-113) Add FreeBSD host class
|
|
279
|
+
|
|
280
|
+
* Also add to supported platforms
|
|
281
|
+
* Main change is location of Puppet data under `/usr/local/etc` rather than `/etc/`
|
|
282
|
+
```
|
|
283
|
+
* (BKR-162) Fix ezbake version string extraction (3b1bf59b)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
(BKR-162) Fix ezbake version string extraction
|
|
288
|
+
|
|
289
|
+
This previously used 'echo -n', which is not a universally available
|
|
290
|
+
feature of echo. When running with an OS X host, this caused the string
|
|
291
|
+
to look like "-n PACKAGE-1.2.3\n", which caused cascading failures in
|
|
292
|
+
the code that uses this data. printf is a more portable replacement.
|
|
293
|
+
```
|
|
294
|
+
* (BKR-161) Beaker tagging improvements (da9fe1ed)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
(BKR-161) Beaker tagging improvements
|
|
299
|
+
|
|
300
|
+
- Use JOB_NAME for 'project' tag default if it exists
|
|
301
|
+
- Set 'beaker_version' tag in vmpooler hypervisor
|
|
302
|
+
```
|
|
303
|
+
* (MAINT) Updates spec to detect error string (58d7a031)
|
|
304
|
+
|
|
305
|
+
* (MAINT) Changes error message to help debugging (2d9bc305)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
(MAINT) Changes error message to help debugging
|
|
310
|
+
|
|
311
|
+
* Changes the wording to say it was not connectable rather than found
|
|
312
|
+
* Adds error string to help debugging the issue
|
|
313
|
+
```
|
|
314
|
+
* (MAINT) Adds non-cygwin block for copy_module_to (8e5cdd0c)
|
|
315
|
+
|
|
316
|
+
### <a name = "beaker2.8.0">beaker2.8.0 - 26 Mar, 2015 (2d25d06d)
|
|
317
|
+
|
|
318
|
+
* (HISTORY) update beaker history for gem release 2.8.0 (2d25d06d)
|
|
80
319
|
|
|
81
320
|
* (GEM) update beaker version to 2.8.0 (f320c276)
|
|
82
321
|
|
data/Rakefile
CHANGED
data/beaker.gemspec
CHANGED
|
@@ -29,7 +29,6 @@ Gem::Specification.new do |s|
|
|
|
29
29
|
s.add_development_dependency 'yard'
|
|
30
30
|
s.add_development_dependency 'markdown'
|
|
31
31
|
s.add_development_dependency 'thin'
|
|
32
|
-
s.add_development_dependency 'gitlab-grit'
|
|
33
32
|
|
|
34
33
|
# Run time dependencies
|
|
35
34
|
s.add_runtime_dependency 'minitest', '~> 5.4'
|
|
@@ -169,7 +169,7 @@ module Beaker
|
|
|
169
169
|
|
|
170
170
|
load 'ezbake.rb'
|
|
171
171
|
ezbake = EZBake::Config
|
|
172
|
-
ezbake[:package_version] = `
|
|
172
|
+
ezbake[:package_version] = `printf $(rake pl:print_build_param[ref] | tail -n 1)`
|
|
173
173
|
EZBakeUtils.config = ezbake
|
|
174
174
|
end
|
|
175
175
|
end
|
|
@@ -13,7 +13,7 @@ module Beaker
|
|
|
13
13
|
# * the module {Beaker::DSL::Wrappers} the provides convenience methods for {Beaker::DSL::Command} creation
|
|
14
14
|
module ModuleUtils
|
|
15
15
|
|
|
16
|
-
# The directories in the module directory that will not be scp-ed to the test system when using
|
|
16
|
+
# The directories in the module directory that will not be scp-ed to the test system when using
|
|
17
17
|
# `copy_module_to`
|
|
18
18
|
PUPPET_MODULE_INSTALL_IGNORE = ['.bundle', '.git', '.idea', '.vagrant', '.vendor', 'vendor', 'acceptance',
|
|
19
19
|
'bundle', 'spec', 'tests', 'log']
|
|
@@ -123,22 +123,24 @@ module Beaker
|
|
|
123
123
|
|
|
124
124
|
opts[:protocol] ||= 'scp'
|
|
125
125
|
case opts[:protocol]
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if
|
|
134
|
-
# NOTE: this will need to be updated to handle powershell only windows SUTs
|
|
126
|
+
when 'scp'
|
|
127
|
+
#move to the host
|
|
128
|
+
scp_to host, source_path, target_module_dir, {:ignore => ignore_list}
|
|
129
|
+
#rename to the selected module name, if not correct
|
|
130
|
+
cur_path = File.join(target_module_dir, source_name)
|
|
131
|
+
new_path = File.join(target_module_dir, module_name)
|
|
132
|
+
if (cur_path != new_path)
|
|
133
|
+
if host.is_cygwin?
|
|
135
134
|
on host, "mv #{cur_path} #{new_path}"
|
|
135
|
+
else
|
|
136
|
+
on host, "move /y #{cur_path} #{new_path}"
|
|
136
137
|
end
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
138
|
+
end
|
|
139
|
+
when 'rsync'
|
|
140
|
+
rsync_to host, source, File.join(target_module_dir, module_name), {:ignore => ignore_list}
|
|
141
|
+
else
|
|
142
|
+
logger.debug "Unsupported transfer protocol, returning nil"
|
|
143
|
+
nil
|
|
142
144
|
end
|
|
143
145
|
end
|
|
144
146
|
end
|
|
@@ -551,7 +551,7 @@ module Beaker
|
|
|
551
551
|
platform_configs_dir = File.join(repo_configs_dir, variant)
|
|
552
552
|
|
|
553
553
|
# some of the uses of dev_builds_url below can't include protocol info,
|
|
554
|
-
#
|
|
554
|
+
# plus this opens up possibility of switching the behavior on provided
|
|
555
555
|
# url type
|
|
556
556
|
_, protocol, hostname = options[:dev_builds_url].partition /.*:\/\//
|
|
557
557
|
dev_builds_url = protocol + hostname
|
|
@@ -583,10 +583,16 @@ module Beaker
|
|
|
583
583
|
repo_filename,
|
|
584
584
|
platform_configs_dir)
|
|
585
585
|
|
|
586
|
-
link = "%s/%s/%s/repos/%s/%s%s/
|
|
586
|
+
link = "%s/%s/%s/repos/%s/%s%s/PC1/%s/" %
|
|
587
587
|
[ dev_builds_url, package_name, build_version, variant,
|
|
588
588
|
fedora_prefix, version, arch ]
|
|
589
589
|
|
|
590
|
+
if not link_exists?( link )
|
|
591
|
+
link = "%s/%s/%s/repos/%s/%s%s/products/%s/" %
|
|
592
|
+
[ dev_builds_url, package_name, build_version, variant,
|
|
593
|
+
fedora_prefix, version, arch ]
|
|
594
|
+
end
|
|
595
|
+
|
|
590
596
|
if not link_exists?( link )
|
|
591
597
|
link = "%s/%s/%s/repos/%s/%s%s/devel/%s/" %
|
|
592
598
|
[ dev_builds_url, package_name, build_version, variant,
|
|
@@ -626,8 +632,13 @@ module Beaker
|
|
|
626
632
|
scp_to host, list, config_dir
|
|
627
633
|
scp_to host, repo_dir, "/root/#{package_name}"
|
|
628
634
|
|
|
635
|
+
pc1_check = on( host,
|
|
636
|
+
"[[ -d /root/#{package_name}/#{codename}/PC1 ]]",
|
|
637
|
+
:acceptable_exit_codes => [0,1] )
|
|
638
|
+
|
|
639
|
+
repo_name = pc1_check.exit_code == 0 ? 'PC1' : 'main'
|
|
629
640
|
search = "deb\\s\\+http:\\/\\/#{hostname}.*$"
|
|
630
|
-
replace = "deb file:\\/\\/\\/root\\/#{package_name}\\/#{codename} #{codename}
|
|
641
|
+
replace = "deb file:\\/\\/\\/root\\/#{package_name}\\/#{codename} #{codename} #{repo_name}"
|
|
631
642
|
sed_command = "sed -i 's/#{search}/#{replace}/'"
|
|
632
643
|
find_and_sed = "find #{config_dir} -name \"*.list\" -exec #{sed_command} {} \\;"
|
|
633
644
|
|
|
@@ -716,6 +727,16 @@ module Beaker
|
|
|
716
727
|
end
|
|
717
728
|
end
|
|
718
729
|
|
|
730
|
+
# This method will install a pem file certifcate on a windows host
|
|
731
|
+
#
|
|
732
|
+
# @param [Host] host A host object
|
|
733
|
+
# @param [String] cert_name The name of the pem file
|
|
734
|
+
# @param [String] cert The contents of the certificate
|
|
735
|
+
#
|
|
736
|
+
def install_cert_on_windows(host, cert_name, cert)
|
|
737
|
+
create_remote_file(host, "C:\\Windows\\Temp\\#{cert_name}.pem", cert)
|
|
738
|
+
on host, "certutil -v -addstore Root C:\\Windows\\Temp\\#{cert_name}.pem"
|
|
739
|
+
end
|
|
719
740
|
end
|
|
720
741
|
end
|
|
721
742
|
end
|
data/lib/beaker/host.rb
CHANGED
|
@@ -39,6 +39,8 @@ module Beaker
|
|
|
39
39
|
Aix::Host.new name, options
|
|
40
40
|
when /osx/
|
|
41
41
|
Mac::Host.new name, options
|
|
42
|
+
when /freebsd/
|
|
43
|
+
FreeBSD::Host.new name, options
|
|
42
44
|
else
|
|
43
45
|
Unix::Host.new name, options
|
|
44
46
|
end
|
|
@@ -394,10 +396,19 @@ module Beaker
|
|
|
394
396
|
unless options[:silent]
|
|
395
397
|
# What?
|
|
396
398
|
result.log(@logger)
|
|
399
|
+
if !options[:expect_connection_failure] && !result.exit_code
|
|
400
|
+
# no exit code was collected, so the stream failed
|
|
401
|
+
raise CommandFailure, "Host '#{self}' connection failure running:\n #{cmdline}\nLast #{@options[:trace_limit]} lines of output were:\n#{result.formatted_output(@options[:trace_limit])}"
|
|
402
|
+
|
|
403
|
+
end
|
|
404
|
+
if options[:expect_connection_failure] && result.exit_code
|
|
405
|
+
# should have had a connection failure, but didn't
|
|
406
|
+
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])}"
|
|
407
|
+
end
|
|
397
408
|
# No, TestCase has the knowledge about whether its failed, checking acceptable
|
|
398
409
|
# exit codes at the host level and then raising...
|
|
399
410
|
# is it necessary to break execution??
|
|
400
|
-
if !options[:accept_all_exit_codes] && !result.exit_code_in?(Array(options[:acceptable_exit_codes] || 0))
|
|
411
|
+
if !options[:accept_all_exit_codes] && !result.exit_code_in?(Array(options[:acceptable_exit_codes] || [0, nil]))
|
|
401
412
|
raise CommandFailure, "Host '#{self}' exited with #{result.exit_code} running:\n #{cmdline}\nLast #{@options[:trace_limit]} lines of output were:\n#{result.formatted_output(@options[:trace_limit])}"
|
|
402
413
|
end
|
|
403
414
|
end
|
|
@@ -595,7 +606,14 @@ module Beaker
|
|
|
595
606
|
|
|
596
607
|
end
|
|
597
608
|
|
|
598
|
-
[
|
|
609
|
+
[
|
|
610
|
+
'windows',
|
|
611
|
+
'pswindows',
|
|
612
|
+
'unix',
|
|
613
|
+
'aix',
|
|
614
|
+
'mac',
|
|
615
|
+
'freebsd',
|
|
616
|
+
].each do |lib|
|
|
599
617
|
require "beaker/host/#{lib}"
|
|
600
618
|
end
|
|
601
619
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[ 'host', 'command_factory' ].each do |lib|
|
|
2
|
+
require "beaker/#{lib}"
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
module FreeBSD
|
|
6
|
+
class Host < Unix::Host
|
|
7
|
+
|
|
8
|
+
def self.foss_defaults
|
|
9
|
+
h = Beaker::Options::OptionsHash.new
|
|
10
|
+
h.merge({
|
|
11
|
+
'user' => 'root',
|
|
12
|
+
'group' => 'puppet',
|
|
13
|
+
'puppetserver-confdir' => '/etc/puppetserver/conf.d',
|
|
14
|
+
'puppetservice' => 'puppetmaster',
|
|
15
|
+
'puppetpath' => '/usr/local/etc/puppet/modules',
|
|
16
|
+
'puppetvardir' => '/var/lib/puppet',
|
|
17
|
+
'puppetbin' => '/usr/bin/puppet',
|
|
18
|
+
'puppetbindir' => '/usr/bin',
|
|
19
|
+
'hieralibdir' => '/opt/puppet-git-repos/hiera/lib',
|
|
20
|
+
'hierapuppetlibdir' => '/opt/puppet-git-repos/hiera-puppet/lib',
|
|
21
|
+
'hierabindir' => '/opt/puppet-git-repos/hiera/bin',
|
|
22
|
+
'hieradatadir' => '/usr/local/etc/puppet/modules/hieradata',
|
|
23
|
+
'hieraconf' => '/usr/local/etc/puppet/modules/hiera.yaml',
|
|
24
|
+
'distmoduledir' => '/usr/local/etc/puppet/modules',
|
|
25
|
+
'sitemoduledir' => '/usr/share/puppet/modules',
|
|
26
|
+
'pathseparator' => ':',
|
|
27
|
+
})
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
data/lib/beaker/host/unix/pkg.rb
CHANGED
|
@@ -32,6 +32,10 @@ module Unix::Pkg
|
|
|
32
32
|
result = exec(Beaker::Command.new("pkg info #{name}"), :acceptable_exit_codes => (0...127))
|
|
33
33
|
when /solaris-10/
|
|
34
34
|
result = exec(Beaker::Command.new("pkginfo #{name}"), :acceptable_exit_codes => (0...127))
|
|
35
|
+
when /freebsd-9/
|
|
36
|
+
result = exec(Beaker::Command.new("pkg_info #{name}"), :acceptable_exit_codes => (0...127))
|
|
37
|
+
when /freebsd-10/
|
|
38
|
+
result = exec(Beaker::Command.new("pkg info #{name}"), :acceptable_exit_codes => (0...127))
|
|
35
39
|
else
|
|
36
40
|
raise "Package #{name} cannot be queried on #{self}"
|
|
37
41
|
end
|
|
@@ -70,6 +74,10 @@ module Unix::Pkg
|
|
|
70
74
|
execute("pkg #{cmdline_args} install #{name}")
|
|
71
75
|
when /solaris-10/
|
|
72
76
|
execute("pkgutil -i -y #{cmdline_args} #{name}")
|
|
77
|
+
when /freebsd-9/
|
|
78
|
+
execute("pkg_add -fr #{cmdline_args} #{name}")
|
|
79
|
+
when /freebsd-10/
|
|
80
|
+
execute("pkg #{cmdline_args} install #{name}")
|
|
73
81
|
else
|
|
74
82
|
raise "Package #{name} cannot be installed on #{self}"
|
|
75
83
|
end
|
|
@@ -200,4 +208,4 @@ module Unix::Pkg
|
|
|
200
208
|
raise "Package repo cannot be deployed on #{self}; the platform is not supported"
|
|
201
209
|
end
|
|
202
210
|
end
|
|
203
|
-
end
|
|
211
|
+
end
|
|
@@ -313,15 +313,18 @@ module Beaker
|
|
|
313
313
|
end
|
|
314
314
|
end
|
|
315
315
|
|
|
316
|
-
#Update /etc/hosts to make it possible for each provided host to reach each other host by name.
|
|
317
|
-
#Assumes that each provided host has host[:ip] set
|
|
316
|
+
# Update /etc/hosts to make it possible for each provided host to reach each other host by name.
|
|
317
|
+
# Assumes that each provided host has host[:ip] set; in the instance where a provider sets
|
|
318
|
+
# host['ip'] to an address which facilitates access to the host externally, but the actual host
|
|
319
|
+
# addresses differ from this, we check first for the presence of a host['vm_ip'] key first,
|
|
320
|
+
# and use that if present.
|
|
318
321
|
# @param [Host, Array<Host>] hosts An array of hosts to act upon
|
|
319
322
|
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
|
320
323
|
# @option opts [Beaker::Logger] :logger A {Beaker::Logger} object
|
|
321
324
|
def hack_etc_hosts hosts, opts
|
|
322
325
|
etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
|
|
323
326
|
hosts.each do |host|
|
|
324
|
-
etc_hosts += "#{host['ip'].to_s}\t#{host[:vmhostname] || host.name}\n"
|
|
327
|
+
etc_hosts += "#{host['vm_ip'] || host['ip'].to_s}\t#{host[:vmhostname] || host.name}\n"
|
|
325
328
|
end
|
|
326
329
|
hosts.each do |host|
|
|
327
330
|
set_etc_hosts(host, etc_hosts)
|
|
@@ -16,7 +16,7 @@ module Beaker
|
|
|
16
16
|
begin
|
|
17
17
|
::Docker.validate_version!
|
|
18
18
|
rescue Excon::Errors::SocketError => e
|
|
19
|
-
raise "Docker instance not
|
|
19
|
+
raise "Docker instance not connectable.\nError was: #{e}\nIf you are on OSX, you might not have Boot2Docker setup correctly\nCheck your DOCKER_HOST variable has been set"
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# Pass on all the logging from docker-api to the beaker logger instance
|
|
@@ -64,7 +64,12 @@ module Beaker
|
|
|
64
64
|
@logger.debug("node available as ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
|
|
65
65
|
host['docker_container'] = container
|
|
66
66
|
host['docker_image'] = image
|
|
67
|
+
host['vm_ip'] = container.json["NetworkSettings"]["IPAddress"].to_s
|
|
68
|
+
|
|
67
69
|
end
|
|
70
|
+
|
|
71
|
+
hack_etc_hosts @hosts, @options
|
|
72
|
+
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
def cleanup
|
|
@@ -111,6 +111,7 @@ module Beaker
|
|
|
111
111
|
@logger.notify 'Tagging vmpooler VMs'
|
|
112
112
|
|
|
113
113
|
tags = {
|
|
114
|
+
'beaker_version' => Beaker::Version::STRING,
|
|
114
115
|
'jenkins_build_url' => @options[:jenkins_build_url],
|
|
115
116
|
'department' => @options[:department],
|
|
116
117
|
'project' => @options[:project],
|
|
@@ -26,7 +26,16 @@ module Beaker
|
|
|
26
26
|
@machines = {}
|
|
27
27
|
@hypervisors = nil
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
# user provided prefix has top priority
|
|
30
|
+
if not @options[:log_prefix]
|
|
31
|
+
# name it after the hosts file
|
|
32
|
+
if @options[:hosts_file]
|
|
33
|
+
@options[:log_prefix] = File.basename(@options[:hosts_file], '.yml')
|
|
34
|
+
else
|
|
35
|
+
#here be the default
|
|
36
|
+
@options[:log_prefix] = @options[:default_log_prefix]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
30
39
|
@options[:timestamp] = Time.now unless @options.has_key?(:timestamp)
|
|
31
40
|
@options[:xml_dated_dir] = Beaker::Logger.generate_dated_log_folder(@options[:xml_dir], @options[:log_prefix], @options[:timestamp])
|
|
32
41
|
@options[:log_dated_dir] = Beaker::Logger.generate_dated_log_folder(@options[:log_dir], @options[:log_prefix], @options[:timestamp])
|
|
@@ -11,9 +11,12 @@ module Beaker
|
|
|
11
11
|
# us to define multiple environment variables for the same
|
|
12
12
|
# configuration value. They are checked in the order they are arrayed
|
|
13
13
|
# so that preferred and "fallback" values work as expected.
|
|
14
|
+
#
|
|
15
|
+
# 'JOB_NAME' and 'BUILD_URL' envs are supplied by Jenkins
|
|
16
|
+
# https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
|
|
14
17
|
ENVIRONMENT_SPEC = {
|
|
15
18
|
:home => 'HOME',
|
|
16
|
-
:project => ['BEAKER_PROJECT', 'BEAKER_project'],
|
|
19
|
+
:project => ['BEAKER_PROJECT', 'BEAKER_project', 'JOB_NAME'],
|
|
17
20
|
:department => ['BEAKER_DEPARTMENT', 'BEAKER_department'],
|
|
18
21
|
:jenkins_build_url => ['BEAKER_BUILD_URL', 'BUILD_URL'],
|
|
19
22
|
:created_by => ['BEAKER_CREATED_BY'],
|
|
@@ -134,6 +137,7 @@ module Beaker
|
|
|
134
137
|
:xml_dir => 'junit',
|
|
135
138
|
:xml_file => 'beaker_junit.xml',
|
|
136
139
|
:xml_stylesheet => 'junit.xsl',
|
|
140
|
+
:default_log_prefix => 'beaker_logs',
|
|
137
141
|
:log_dir => 'log',
|
|
138
142
|
:log_sut_event => 'sut.log',
|
|
139
143
|
:color => true,
|
data/lib/beaker/platform.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Beaker
|
|
|
3
3
|
# all String methods while adding several platform-specific use cases.
|
|
4
4
|
class Platform < String
|
|
5
5
|
# Supported platforms
|
|
6
|
-
PLATFORMS = /^(osx|centos|fedora|debian|oracle|redhat|scientific|sles|ubuntu|windows|solaris|aix|el|eos|cumulus)\-.+\-.+$/
|
|
6
|
+
PLATFORMS = /^(freebsd|osx|centos|fedora|debian|oracle|redhat|scientific|sles|ubuntu|windows|solaris|aix|el|eos|cumulus)\-.+\-.+$/
|
|
7
7
|
|
|
8
8
|
# Platform version numbers vs. codenames conversion hash
|
|
9
9
|
PLATFORM_VERSION_CODES =
|
data/lib/beaker/result.rb
CHANGED
|
@@ -18,6 +18,7 @@ module Beaker
|
|
|
18
18
|
Errno::ENETUNREACH,
|
|
19
19
|
Net::SSH::Disconnect,
|
|
20
20
|
Net::SSH::AuthenticationFailed,
|
|
21
|
+
IOError,
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
def initialize hostname, user = nil, ssh_opts = {}, options = {}
|
|
@@ -34,38 +35,47 @@ module Beaker
|
|
|
34
35
|
connection
|
|
35
36
|
end
|
|
36
37
|
|
|
38
|
+
# connect to the host
|
|
37
39
|
def connect
|
|
38
40
|
try = 1
|
|
39
41
|
last_wait = 0
|
|
40
42
|
wait = 1
|
|
41
43
|
@ssh ||= begin
|
|
44
|
+
@logger.debug "Attempting ssh connection to #{@hostname}, user: #{@user}, opts: #{@ssh_opts}"
|
|
42
45
|
Net::SSH.start(@hostname, @user, @ssh_opts)
|
|
43
46
|
rescue *RETRYABLE_EXCEPTIONS => e
|
|
44
47
|
if try <= 11
|
|
45
|
-
@logger.warn "Try #{try} -- Host #{@hostname} unreachable: #{e.message}"
|
|
48
|
+
@logger.warn "Try #{try} -- Host #{@hostname} unreachable: #{e.class.name} - #{e.message}"
|
|
46
49
|
@logger.warn "Trying again in #{wait} seconds"
|
|
47
50
|
sleep wait
|
|
48
51
|
(last_wait, wait) = wait, last_wait + wait
|
|
49
52
|
try += 1
|
|
50
53
|
retry
|
|
51
54
|
else
|
|
52
|
-
# why is the logger not passed into this class?
|
|
53
55
|
@logger.error "Failed to connect to #{@hostname}"
|
|
54
56
|
raise
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
|
-
@logger.debug "Created ssh connection to #{@hostname}, user: #{@user}, opts: #{@ssh_opts}"
|
|
58
|
-
self
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# closes this SshConnection
|
|
62
62
|
def close
|
|
63
63
|
begin
|
|
64
|
-
@ssh
|
|
65
|
-
|
|
64
|
+
if @ssh and not @ssh.closed?
|
|
65
|
+
@ssh.close
|
|
66
|
+
else
|
|
67
|
+
@logger.warn("ssh.close: connection is already closed, no action needed")
|
|
68
|
+
end
|
|
69
|
+
rescue *RETRYABLE_EXCEPTIONS => e
|
|
70
|
+
@logger.warn "Attemped ssh.close, (caught #{e.class.name} - #{e.message})."
|
|
71
|
+
rescue => e
|
|
72
|
+
@logger.warn "ssh.close threw unexpected Error: #{e.class.name} - #{e.message}. Shutting down, and re-raising error below"
|
|
66
73
|
@ssh.shutdown!
|
|
74
|
+
raise e
|
|
75
|
+
ensure
|
|
76
|
+
@ssh = nil
|
|
77
|
+
@logger.warn("ssh connection to #{@hostname} has been terminated")
|
|
67
78
|
end
|
|
68
|
-
@ssh = nil
|
|
69
79
|
end
|
|
70
80
|
|
|
71
81
|
def try_to_execute command, options = {}, stdout_callback = nil,
|
|
@@ -93,7 +103,13 @@ module Beaker
|
|
|
93
103
|
|
|
94
104
|
# Process SSH activity until we stop doing that - which is when our
|
|
95
105
|
# channel is finished with...
|
|
96
|
-
|
|
106
|
+
begin
|
|
107
|
+
@ssh.loop
|
|
108
|
+
rescue *RETRYABLE_EXCEPTIONS => e
|
|
109
|
+
# this would indicate that the connection failed post execution, since the channel exec was successful
|
|
110
|
+
@logger.warn "ssh channel on #{@hostname} received exception post command execution #{e.class.name} - #{e.message}"
|
|
111
|
+
close
|
|
112
|
+
end
|
|
97
113
|
|
|
98
114
|
result.finalize!
|
|
99
115
|
@logger.last_result = result
|
|
@@ -102,15 +118,21 @@ module Beaker
|
|
|
102
118
|
|
|
103
119
|
def execute command, options = {}, stdout_callback = nil,
|
|
104
120
|
stderr_callback = stdout_callback
|
|
105
|
-
|
|
121
|
+
try = 1
|
|
122
|
+
wait = 1
|
|
123
|
+
last_wait = 0
|
|
106
124
|
begin
|
|
125
|
+
# ensure that we have a current connection object
|
|
126
|
+
connect
|
|
107
127
|
result = try_to_execute(command, options, stdout_callback, stderr_callback)
|
|
108
128
|
rescue *RETRYABLE_EXCEPTIONS => e
|
|
109
|
-
if
|
|
110
|
-
|
|
111
|
-
|
|
129
|
+
if try < 11
|
|
130
|
+
sleep wait
|
|
131
|
+
(last_wait, wait) = wait, last_wait + wait
|
|
132
|
+
try += 1
|
|
133
|
+
@logger.error "Command execution '#{@hostname}$ #{command}' failed (#{e.class.name} - #{e.message})"
|
|
112
134
|
close
|
|
113
|
-
|
|
135
|
+
@logger.debug "Preparing to retry: closed ssh object"
|
|
114
136
|
retry
|
|
115
137
|
else
|
|
116
138
|
raise
|
data/lib/beaker/version.rb
CHANGED
|
@@ -118,6 +118,7 @@ describe ClassMixedWithDSLInstallUtils do
|
|
|
118
118
|
it{
|
|
119
119
|
host = double("host")
|
|
120
120
|
allow( host ).to receive(:[]).with('distmoduledir').and_return('/etc/puppetlabs/puppet/modules')
|
|
121
|
+
allow( host ).to receive(:is_cygwin?).and_return(true)
|
|
121
122
|
result = double
|
|
122
123
|
stdout = target.split('/')[0..-2].join('/') + "\n"
|
|
123
124
|
allow( result ).to receive(:stdout).and_return( stdout )
|
|
@@ -167,12 +168,30 @@ describe ClassMixedWithDSLInstallUtils do
|
|
|
167
168
|
allow( subject ).to receive( :build_ignore_list ).and_return( [] )
|
|
168
169
|
allow( subject ).to receive( :parse_for_modulename ).and_return( [nil, 'modulename'] )
|
|
169
170
|
allow( subject ).to receive( :on ).and_return( double.as_null_object )
|
|
170
|
-
hosts = [{}, {}]
|
|
171
171
|
|
|
172
|
-
expect( subject ).to receive( :scp_to ).
|
|
172
|
+
expect( subject ).to receive( :scp_to ).exactly(4).times
|
|
173
173
|
subject.copy_module_to( hosts )
|
|
174
174
|
end
|
|
175
175
|
end
|
|
176
|
+
|
|
177
|
+
describe 'non-cygwin windows' do
|
|
178
|
+
it 'should have different commands than cygwin' do
|
|
179
|
+
host = double("host")
|
|
180
|
+
allow( host ).to receive(:[]).with('platform').and_return('windows')
|
|
181
|
+
allow( host ).to receive(:[]).with('distmoduledir').and_return('C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules')
|
|
182
|
+
allow( host ).to receive(:is_cygwin?).and_return(false)
|
|
183
|
+
|
|
184
|
+
result = double
|
|
185
|
+
allow( result ).to receive(:stdout).and_return( 'C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules' )
|
|
186
|
+
|
|
187
|
+
expect( subject ).to receive(:on).with(host, "echo C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules" ).and_return( result )
|
|
188
|
+
|
|
189
|
+
expect( subject ).to receive(:scp_to).with(host, "/opt/testmodule2", "C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules", {:ignore => ignore_list})
|
|
190
|
+
expect( subject ).to receive(:on).with(host, 'move /y C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules/testmodule2 C:\\ProgramData\\PuppetLabs\\puppet\\etc\\modules/testmodule')
|
|
191
|
+
|
|
192
|
+
subject.copy_module_to(host, {:module_name => 'testmodule', :source => '/opt/testmodule2'})
|
|
193
|
+
end
|
|
194
|
+
end
|
|
176
195
|
end
|
|
177
196
|
|
|
178
197
|
describe 'split_author_modulename' do
|
|
@@ -344,18 +344,10 @@ describe ClassMixedWithDSLInstallUtils do
|
|
|
344
344
|
end
|
|
345
345
|
|
|
346
346
|
RSpec.shared_examples "install-dev-repo" do
|
|
347
|
-
let( :repo_config ) { "repoconfig" }
|
|
348
|
-
let( :repo_dir ) { "repodir" }
|
|
349
|
-
|
|
350
|
-
before do
|
|
351
|
-
allow(subject).to receive(:fetch_http_file) { repo_config }
|
|
352
|
-
allow(subject).to receive(:fetch_http_dir) { repo_dir }
|
|
353
|
-
allow(subject).to receive(:on).with(host, "apt-get update") { }
|
|
354
|
-
allow(subject).to receive(:options) { opts }
|
|
355
|
-
allow(subject).to receive(:link_exists?) { true }
|
|
356
|
-
end
|
|
357
347
|
|
|
358
348
|
it "scp's files to SUT then modifies them with find-and-sed 2-hit combo" do
|
|
349
|
+
allow(rez).to receive(:exit_code) { 0 }
|
|
350
|
+
allow(subject).to receive(:link_exists?).and_return(true)
|
|
359
351
|
expect(subject).to receive(:on).with( host, /^mkdir -p .*$/ ).ordered
|
|
360
352
|
expect(subject).to receive(:scp_to).with( host, repo_config, /.*/ ).ordered
|
|
361
353
|
expect(subject).to receive(:scp_to).with( host, repo_dir, /.*/ ).ordered
|
|
@@ -382,19 +374,72 @@ describe ClassMixedWithDSLInstallUtils do
|
|
|
382
374
|
subject.install_puppetlabs_dev_repo host, package_name, package_version
|
|
383
375
|
}.to raise_error(RuntimeError, /No repository installation step for/)
|
|
384
376
|
end
|
|
385
|
-
|
|
386
377
|
end
|
|
387
378
|
|
|
388
|
-
describe
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
379
|
+
describe 'When on supported platforms' do
|
|
380
|
+
# These are not factored into the `before` block above because they
|
|
381
|
+
# are expectations in the share examples, but aren't interesting
|
|
382
|
+
# beyond those basic tests
|
|
383
|
+
def stub_uninteresting_portions_of_install_puppetlabs_dev_repo!
|
|
384
|
+
allow(subject).to receive(:on).with( host, /^mkdir -p .*$/ ).ordered
|
|
385
|
+
allow(subject).to receive(:scp_to).with( host, repo_config, /.*/ ).ordered
|
|
386
|
+
allow(subject).to receive(:scp_to).with( host, repo_dir, /.*/ ).ordered
|
|
387
|
+
end
|
|
392
388
|
|
|
393
|
-
|
|
394
|
-
let( :
|
|
395
|
-
|
|
396
|
-
|
|
389
|
+
let( :repo_config ) { "repoconfig" }
|
|
390
|
+
let( :repo_dir ) { "repodir" }
|
|
391
|
+
let( :rez ) { double }
|
|
392
|
+
|
|
393
|
+
before do
|
|
394
|
+
allow(subject).to receive(:fetch_http_file) { repo_config }
|
|
395
|
+
allow(subject).to receive(:fetch_http_dir) { repo_dir }
|
|
396
|
+
allow(subject).to receive(:on).with(host, "apt-get update") { }
|
|
397
|
+
allow(subject).to receive(:options) { opts }
|
|
398
|
+
allow(subject).to receive(:on).with( host, /^.* -d .*/, {:acceptable_exit_codes =>[0,1]} ).and_return(rez)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
describe "that are debian-like" do
|
|
402
|
+
let( :platform ) { Beaker::Platform.new('debian-7-i386') }
|
|
403
|
+
before { allow(subject).to receive(:link_exists?).and_return(true) }
|
|
404
|
+
|
|
405
|
+
include_examples "install-dev-repo"
|
|
397
406
|
|
|
407
|
+
it 'sets up the PC1 repository if that was downloaded' do
|
|
408
|
+
allow(rez).to receive(:exit_code) { 0 }
|
|
409
|
+
|
|
410
|
+
stub_uninteresting_portions_of_install_puppetlabs_dev_repo!
|
|
411
|
+
|
|
412
|
+
expect(subject).to receive(:on).with( host, /^find .* sed .*PC1.*/ )
|
|
413
|
+
subject.install_puppetlabs_dev_repo host, package_name, package_version
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
it 'sets up the main repository if that was downloaded' do
|
|
417
|
+
allow(rez).to receive(:exit_code) { 1 }
|
|
418
|
+
|
|
419
|
+
stub_uninteresting_portions_of_install_puppetlabs_dev_repo!
|
|
420
|
+
|
|
421
|
+
expect(subject).to receive(:on).with( host, /^find .* sed .*main.*/ )
|
|
422
|
+
subject.install_puppetlabs_dev_repo host, package_name, package_version
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
describe "that are redhat-like" do
|
|
428
|
+
let( :platform ) { Beaker::Platform.new('el-7-i386') }
|
|
429
|
+
include_examples "install-dev-repo"
|
|
430
|
+
|
|
431
|
+
it 'downloads PC1, products, or devel repo -- in that order' do
|
|
432
|
+
allow(subject).to receive(:on).with( host, /^find .* sed .*/ )
|
|
433
|
+
stub_uninteresting_portions_of_install_puppetlabs_dev_repo!
|
|
434
|
+
|
|
435
|
+
expect(subject).to receive(:link_exists?).with(/.*PC1.*/).and_return( false )
|
|
436
|
+
expect(subject).to receive(:link_exists?).with(/.*products.*/).and_return( false )
|
|
437
|
+
expect(subject).to receive(:link_exists?).with(/.*devel.*/).and_return( true )
|
|
438
|
+
|
|
439
|
+
subject.install_puppetlabs_dev_repo host, package_name, package_version
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
end
|
|
398
443
|
end
|
|
399
444
|
|
|
400
445
|
describe '#install_packages_from_local_dev_repo' do
|
|
@@ -518,6 +563,52 @@ describe ClassMixedWithDSLInstallUtils do
|
|
|
518
563
|
|
|
519
564
|
subject.install_puppetagent_dev_repo( host, opts )
|
|
520
565
|
end
|
|
566
|
+
end
|
|
521
567
|
|
|
568
|
+
describe '#install_cert_on_windows' do
|
|
569
|
+
before do
|
|
570
|
+
subject.stub(:on).and_return(Beaker::Result.new({},''))
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
context 'on windows' do
|
|
574
|
+
let(:platform) { 'windows-2008R2-amd64' }
|
|
575
|
+
let(:host) { make_host('testbox.test.local', :platform => 'windows-2008R2-amd64') }
|
|
576
|
+
|
|
577
|
+
it 'should install all 3 certs' do
|
|
578
|
+
cert = 'geotrust_global_ca'
|
|
579
|
+
content = <<-EOM
|
|
580
|
+
-----BEGIN CERTIFICATE-----
|
|
581
|
+
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
|
582
|
+
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
|
583
|
+
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
|
|
584
|
+
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
|
|
585
|
+
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
|
|
586
|
+
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
|
|
587
|
+
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
|
|
588
|
+
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
|
|
589
|
+
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
|
|
590
|
+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
|
|
591
|
+
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
|
|
592
|
+
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
|
|
593
|
+
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
|
|
594
|
+
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
|
|
595
|
+
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
|
|
596
|
+
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
|
|
597
|
+
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
|
|
598
|
+
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
|
|
599
|
+
-----END CERTIFICATE-----
|
|
600
|
+
EOM
|
|
601
|
+
|
|
602
|
+
expect(subject).to receive(:create_remote_file) do |host, file_path, file_content|
|
|
603
|
+
expect(file_path).to eq("C:\\Windows\\Temp\\#{cert}.pem")
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
expect(subject).to receive(:on) do |host, command|
|
|
607
|
+
expect(command).to eq("certutil -v -addstore Root C:\\Windows\\Temp\\#{cert}.pem")
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
subject.install_cert_on_windows(host, cert, content)
|
|
611
|
+
end
|
|
612
|
+
end
|
|
522
613
|
end
|
|
523
614
|
end
|
|
@@ -78,7 +78,8 @@ module Beaker
|
|
|
78
78
|
allow( ::Docker ).to receive(:validate_version!).and_raise(Excon::Errors::SocketError.new( StandardError.new('oops') ))
|
|
79
79
|
end
|
|
80
80
|
it 'should fail when docker not present' do
|
|
81
|
-
expect { docker }.to raise_error(RuntimeError, /Docker instance not
|
|
81
|
+
expect { docker }.to raise_error(RuntimeError, /Docker instance not connectable./)
|
|
82
|
+
expect { docker }.to raise_error(RuntimeError, /Error was: oops/)
|
|
82
83
|
end
|
|
83
84
|
end
|
|
84
85
|
|
|
@@ -198,6 +199,15 @@ module Beaker
|
|
|
198
199
|
|
|
199
200
|
end
|
|
200
201
|
|
|
202
|
+
it "should generate a new /etc/hosts file referencing each host" do
|
|
203
|
+
ENV['DOCKER_HOST'] = nil
|
|
204
|
+
docker.provision
|
|
205
|
+
hosts.each do |host|
|
|
206
|
+
expect( docker ).to receive( :set_etc_hosts ).with( host, "127.0.0.1\tlocalhost localhost.localdomain\n192.0.2.1\tvm1\n192.0.2.1\tvm2\n192.0.2.1\tvm3\n" ).once
|
|
207
|
+
end
|
|
208
|
+
docker.hack_etc_hosts( hosts, options )
|
|
209
|
+
end
|
|
210
|
+
|
|
201
211
|
it 'should record the image and container for later' do
|
|
202
212
|
docker.provision
|
|
203
213
|
|
|
@@ -11,7 +11,9 @@ module Beaker
|
|
|
11
11
|
make_opts.merge({
|
|
12
12
|
'logger' => double().as_null_object,
|
|
13
13
|
:logger_sut => mock_provisioning_logger,
|
|
14
|
-
:log_prefix =>
|
|
14
|
+
:log_prefix => @log_prefix,
|
|
15
|
+
:hosts_file => @hosts_file,
|
|
16
|
+
:default_log_prefix => 'hello_default',
|
|
15
17
|
})
|
|
16
18
|
}
|
|
17
19
|
let( :network_manager ) { NetworkManager.new(options, options[:logger]) }
|
|
@@ -19,6 +21,10 @@ module Beaker
|
|
|
19
21
|
let( :host ) { hosts[0] }
|
|
20
22
|
|
|
21
23
|
describe '#log_sut_event' do
|
|
24
|
+
before :each do
|
|
25
|
+
@log_prefix = 'log_prefix_dummy'
|
|
26
|
+
@hosts_file = 'dummy_hosts'
|
|
27
|
+
end
|
|
22
28
|
|
|
23
29
|
it 'creates the correct content for an event' do
|
|
24
30
|
log_line = network_manager.log_sut_event host, true
|
|
@@ -54,5 +60,29 @@ module Beaker
|
|
|
54
60
|
expect{ nm.log_sut_event(host, true) }.to raise_error(ArgumentError)
|
|
55
61
|
end
|
|
56
62
|
end
|
|
63
|
+
|
|
64
|
+
it 'uses user defined log prefix if it is provided' do
|
|
65
|
+
@log_prefix = 'dummy_log_prefix'
|
|
66
|
+
@hosts_file = 'dummy_hosts'
|
|
67
|
+
nm = network_manager
|
|
68
|
+
cur_prefix = options[:log_prefix]
|
|
69
|
+
expect(cur_prefix).to be === @log_prefix
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'uses host based log prefix, when there is not user defined log prefix' do
|
|
73
|
+
@log_prefix = nil
|
|
74
|
+
@hosts_file = 'dummy_hosts'
|
|
75
|
+
nm = network_manager
|
|
76
|
+
cur_prefix = options[:log_prefix]
|
|
77
|
+
expect(cur_prefix).to be === @hosts_file
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'uses default log prefix, when there is no user defined and no host file' do
|
|
81
|
+
@log_prefix = nil
|
|
82
|
+
@hosts_file = nil
|
|
83
|
+
nm = network_manager
|
|
84
|
+
cur_prefix = options[:log_prefix]
|
|
85
|
+
expect(cur_prefix).to be === 'hello_default'
|
|
86
|
+
end
|
|
57
87
|
end
|
|
58
|
-
end
|
|
88
|
+
end
|
|
@@ -9,6 +9,10 @@ module Beaker
|
|
|
9
9
|
let( :options ) { { :logger => double('logger').as_null_object } }
|
|
10
10
|
subject(:connection) { SshConnection.new host, user, ssh_opts, options }
|
|
11
11
|
|
|
12
|
+
before :each do
|
|
13
|
+
allow( subject ).to receive(:sleep)
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
it 'self.connect creates connects and returns a proxy for that connection' do
|
|
13
17
|
# grrr
|
|
14
18
|
expect( Net::SSH ).to receive(:start).with( host, user, ssh_opts )
|
|
@@ -27,34 +31,42 @@ module Beaker
|
|
|
27
31
|
connection.connect
|
|
28
32
|
end
|
|
29
33
|
|
|
30
|
-
|
|
31
|
-
mock_ssh = Object.new
|
|
32
|
-
expect( Net::SSH ).to receive( :start ).with( host, user, ssh_opts) { mock_ssh }
|
|
33
|
-
connection.connect
|
|
34
|
+
describe '#close' do
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
it 'runs ssh close' do
|
|
37
|
+
mock_ssh = Object.new
|
|
38
|
+
expect( Net::SSH ).to receive( :start ).with( host, user, ssh_opts) { mock_ssh }
|
|
39
|
+
connection.connect
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
allow( mock_ssh).to receive( :closed? ).once.and_return(false)
|
|
42
|
+
expect( mock_ssh ).to receive( :close ).once
|
|
43
|
+
connection.close
|
|
44
|
+
end
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
it 'sets the @ssh variable to nil' do
|
|
47
|
+
mock_ssh = Object.new
|
|
48
|
+
expect( Net::SSH ).to receive( :start ).with( host, user, ssh_opts) { mock_ssh }
|
|
49
|
+
connection.connect
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
allow( mock_ssh).to receive( :closed? ).once.and_return(false)
|
|
52
|
+
expect( mock_ssh ).to receive( :close ).once
|
|
53
|
+
connection.close
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
expect( connection.instance_variable_get(:@ssh) ).to be_nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'calls ssh shutdown & re-raises if ssh close fails with an unexpected Error' do
|
|
59
|
+
mock_ssh = Object.new
|
|
60
|
+
allow( mock_ssh ).to receive( :close ) { raise StandardError }
|
|
61
|
+
expect( Net::SSH ).to receive( :start ).with( host, user, ssh_opts) { mock_ssh }
|
|
62
|
+
connection.connect
|
|
63
|
+
|
|
64
|
+
allow( mock_ssh).to receive( :closed? ).once.and_return(false)
|
|
65
|
+
expect( mock_ssh ).to receive( :shutdown! ).once
|
|
66
|
+
expect{ connection.close }.to raise_error(StandardError)
|
|
67
|
+
expect( connection.instance_variable_get(:@ssh) ).to be_nil
|
|
68
|
+
end
|
|
55
69
|
|
|
56
|
-
expect( mock_ssh ).to receive( :shutdown! ).once
|
|
57
|
-
connection.close
|
|
58
70
|
end
|
|
59
71
|
|
|
60
72
|
describe '#execute' do
|
data/spec/spec_helper.rb
CHANGED
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.9.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-
|
|
11
|
+
date: 2015-04-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|
|
@@ -136,20 +136,6 @@ dependencies:
|
|
|
136
136
|
- - ! '>='
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
138
|
version: '0'
|
|
139
|
-
- !ruby/object:Gem::Dependency
|
|
140
|
-
name: gitlab-grit
|
|
141
|
-
requirement: !ruby/object:Gem::Requirement
|
|
142
|
-
requirements:
|
|
143
|
-
- - ! '>='
|
|
144
|
-
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0'
|
|
146
|
-
type: :development
|
|
147
|
-
prerelease: false
|
|
148
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
-
requirements:
|
|
150
|
-
- - ! '>='
|
|
151
|
-
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0'
|
|
153
139
|
- !ruby/object:Gem::Dependency
|
|
154
140
|
name: minitest
|
|
155
141
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -397,7 +383,6 @@ files:
|
|
|
397
383
|
- beaker.gemspec
|
|
398
384
|
- bin/beaker
|
|
399
385
|
- ext/completion/beaker-completion.bash
|
|
400
|
-
- history.rb
|
|
401
386
|
- lib/beaker.rb
|
|
402
387
|
- lib/beaker/answers.rb
|
|
403
388
|
- lib/beaker/answers/version20.rb
|
|
@@ -435,6 +420,7 @@ files:
|
|
|
435
420
|
- lib/beaker/host/aix/file.rb
|
|
436
421
|
- lib/beaker/host/aix/group.rb
|
|
437
422
|
- lib/beaker/host/aix/user.rb
|
|
423
|
+
- lib/beaker/host/freebsd.rb
|
|
438
424
|
- lib/beaker/host/mac.rb
|
|
439
425
|
- lib/beaker/host/mac/group.rb
|
|
440
426
|
- lib/beaker/host/mac/user.rb
|
data/history.rb
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# Generates a HISTORY.md file for your repo based on tags and commits, defaults to history for
|
|
2
|
-
# master branch but can generate history for specified branch
|
|
3
|
-
#
|
|
4
|
-
# Requires: gem install gitlab-grit
|
|
5
|
-
# Usage: ruby history.rb /your/repo/directory
|
|
6
|
-
#
|
|
7
|
-
# Based on https://coderwall.com/p/99mjcg
|
|
8
|
-
|
|
9
|
-
require 'grit'
|
|
10
|
-
|
|
11
|
-
if ARGV.size < 1
|
|
12
|
-
p "Usage: ruby history.rb /your/repo/directory branch(defaults to master)"
|
|
13
|
-
exit
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
output_file = 'HISTORY.md'
|
|
17
|
-
repo_dir = ARGV[0]
|
|
18
|
-
branch = ARGV[1] || 'master'
|
|
19
|
-
output = "# #{File.basename(File.absolute_path(repo_dir))} - History\n"
|
|
20
|
-
|
|
21
|
-
repo = Grit::Repo.new(repo_dir)
|
|
22
|
-
head = Grit::Tag.new(repo.commits(branch).first.sha, repo, repo.commits(branch).first.id)
|
|
23
|
-
tags = repo.tags + [head]
|
|
24
|
-
tags.sort! {|x,y| y.commit.authored_date <=> x.commit.authored_date}
|
|
25
|
-
|
|
26
|
-
output << "## Tags\n"
|
|
27
|
-
tags.each do |tag|
|
|
28
|
-
tag_name = tag.name
|
|
29
|
-
if tag == tags.first
|
|
30
|
-
tag_name = 'LATEST'
|
|
31
|
-
end
|
|
32
|
-
output << "* [#{tag_name} - #{tag.commit.authored_date.strftime("%-d %b, %Y")} (#{tag.commit.sha[0,8]})](##{tag_name})\n"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
output << "\n## Details\n"
|
|
36
|
-
tagcount = 0
|
|
37
|
-
tags.each do |tag|
|
|
38
|
-
tag_name = tag.name
|
|
39
|
-
if tag == tags.first
|
|
40
|
-
tag_name = 'LATEST'
|
|
41
|
-
end
|
|
42
|
-
output << "### <a name = \"#{tag_name}\">#{tag_name} - #{tag.commit.authored_date.strftime("%-d %b, %Y")} (#{tag.commit.sha[0,8]})\n\n"
|
|
43
|
-
if (tagcount != tags.size - 1)
|
|
44
|
-
commit_set = repo.commits_between(tags[tagcount + 1].name, tag.name)
|
|
45
|
-
commit_set.sort! {|x,y| y.authored_date <=> x.authored_date }
|
|
46
|
-
commit_set.each do |c|
|
|
47
|
-
output << "* #{c.short_message} (#{c.sha[0,8]})\n\n"
|
|
48
|
-
if c.short_message != c.message
|
|
49
|
-
output << "\n```\n#{c.message.gsub(/```/, "\n")}\n```\n"
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
else
|
|
53
|
-
output << "* Initial release.\n"
|
|
54
|
-
end
|
|
55
|
-
tagcount += 1
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
File.open(output_file, 'w') { |f| f.write(output) }
|
|
59
|
-
|
|
60
|
-
puts "success, output sent to #{output_file}"
|