beaker 2.7.1 → 2.8.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/HISTORY.md +121 -2
- data/lib/beaker/dsl.rb +2 -2
- data/lib/beaker/dsl/helpers.rb +13 -1429
- data/lib/beaker/dsl/helpers/facter_helpers.rb +48 -0
- data/lib/beaker/dsl/helpers/hiera_helpers.rb +71 -0
- data/lib/beaker/dsl/helpers/host_helpers.rb +506 -0
- data/lib/beaker/dsl/helpers/puppet_helpers.rb +698 -0
- data/lib/beaker/dsl/helpers/tk_helpers.rb +101 -0
- data/lib/beaker/dsl/helpers/web_helpers.rb +115 -0
- data/lib/beaker/dsl/install_utils.rb +8 -1570
- data/lib/beaker/dsl/install_utils/ezbake_utils.rb +256 -0
- data/lib/beaker/dsl/install_utils/module_utils.rb +237 -0
- data/lib/beaker/dsl/install_utils/pe_utils.rb +518 -0
- data/lib/beaker/dsl/install_utils/puppet_utils.rb +722 -0
- data/lib/beaker/dsl/outcomes.rb +0 -4
- data/lib/beaker/dsl/roles.rb +0 -3
- data/lib/beaker/dsl/structure.rb +127 -4
- data/lib/beaker/dsl/wrappers.rb +0 -4
- data/lib/beaker/host.rb +23 -0
- data/lib/beaker/host/unix/pkg.rb +4 -4
- data/lib/beaker/host_prebuilt_steps.rb +11 -5
- data/lib/beaker/hypervisor/vagrant.rb +1 -0
- data/lib/beaker/hypervisor/vmpooler.rb +38 -0
- data/lib/beaker/logger.rb +10 -4
- data/lib/beaker/network_manager.rb +5 -4
- data/lib/beaker/options/command_line_parser.rb +7 -0
- data/lib/beaker/shared.rb +2 -1
- data/lib/beaker/shared/semvar.rb +41 -0
- data/lib/beaker/test_suite.rb +20 -6
- data/lib/beaker/version.rb +1 -1
- data/spec/beaker/dsl/helpers/facter_helpers_spec.rb +59 -0
- data/spec/beaker/dsl/helpers/hiera_helpers_spec.rb +96 -0
- data/spec/beaker/dsl/helpers/host_helpers_spec.rb +413 -0
- data/spec/beaker/dsl/{helpers_spec.rb → helpers/puppet_helpers_spec.rb} +2 -611
- data/spec/beaker/dsl/helpers/tk_helpers_spec.rb +83 -0
- data/spec/beaker/dsl/helpers/web_helpers_spec.rb +60 -0
- data/spec/beaker/dsl/install_utils/module_utils_spec.rb +241 -0
- data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +475 -0
- data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +523 -0
- data/spec/beaker/dsl/structure_spec.rb +108 -0
- data/spec/beaker/host_prebuilt_steps_spec.rb +44 -0
- data/spec/beaker/host_spec.rb +41 -0
- data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
- data/spec/beaker/logger_spec.rb +9 -2
- data/spec/beaker/network_manager_spec.rb +7 -1
- data/spec/beaker/options/command_line_parser_spec.rb +3 -2
- data/spec/beaker/shared/semvar_spec.rb +36 -0
- data/spec/beaker/test_suite_spec.rb +48 -0
- data/spec/mocks.rb +10 -0
- metadata +23 -5
- data/lib/beaker/dsl/ezbake_utils.rb +0 -259
- data/spec/beaker/dsl/install_utils_spec.rb +0 -1242
@@ -0,0 +1,698 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'inifile'
|
3
|
+
require 'resolv'
|
4
|
+
|
5
|
+
module Beaker
|
6
|
+
module DSL
|
7
|
+
module Helpers
|
8
|
+
# Methods that help you interact with your puppet installation, puppet must be installed
|
9
|
+
# for these methods to execute correctly
|
10
|
+
module PuppetHelpers
|
11
|
+
|
12
|
+
# @!macro common_opts
|
13
|
+
# @param [Hash{Symbol=>String}] opts Options to alter execution.
|
14
|
+
# @option opts [Boolean] :silent (false) Do not produce log output
|
15
|
+
# @option opts [Array<Fixnum>] :acceptable_exit_codes ([0]) An array
|
16
|
+
# (or range) of integer exit codes that should be considered
|
17
|
+
# acceptable. An error will be thrown if the exit code does not
|
18
|
+
# match one of the values in this list.
|
19
|
+
# @option opts [Hash{String=>String}] :environment ({}) These will be
|
20
|
+
# treated as extra environment variables that should be set before
|
21
|
+
# running the command.
|
22
|
+
#
|
23
|
+
|
24
|
+
|
25
|
+
# Return the name of the puppet user.
|
26
|
+
#
|
27
|
+
# @param [Host] host One object that acts like a Beaker::Host
|
28
|
+
#
|
29
|
+
# @note This method assumes puppet is installed on the host.
|
30
|
+
#
|
31
|
+
def puppet_user(host)
|
32
|
+
return host.puppet('master')['user']
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the name of the puppet group.
|
36
|
+
#
|
37
|
+
# @param [Host] host One object that acts like a Beaker::Host
|
38
|
+
#
|
39
|
+
# @note This method assumes puppet is installed on the host.
|
40
|
+
#
|
41
|
+
def puppet_group(host)
|
42
|
+
return host.puppet('master')['group']
|
43
|
+
end
|
44
|
+
|
45
|
+
# Test Puppet running in a certain run mode with specific options.
|
46
|
+
# This ensures the following steps are performed:
|
47
|
+
# 1. The pre-test Puppet configuration is backed up
|
48
|
+
# 2. A new Puppet configuraton file is layed down
|
49
|
+
# 3. Puppet is started or restarted in the specified run mode
|
50
|
+
# 4. Ensure Puppet has started correctly
|
51
|
+
# 5. Further tests are yielded to
|
52
|
+
# 6. Revert Puppet to the pre-test state
|
53
|
+
# 7. Testing artifacts are saved in a folder named for the test
|
54
|
+
#
|
55
|
+
# @param [Host] host One object that act like Host
|
56
|
+
#
|
57
|
+
# @param [Hash{Symbol=>String}] conf_opts Represents puppet settings.
|
58
|
+
# Sections of the puppet.conf may be
|
59
|
+
# specified, if no section is specified the
|
60
|
+
# a puppet.conf file will be written with the
|
61
|
+
# options put in a section named after [mode]
|
62
|
+
# @option conf_opts [String] :__commandline_args__ A special setting for
|
63
|
+
# command_line arguments such as --debug or
|
64
|
+
# --logdest, which cannot be set in
|
65
|
+
# puppet.conf. For example:
|
66
|
+
#
|
67
|
+
# :__commandline_args__ => '--logdest /tmp/a.log'
|
68
|
+
#
|
69
|
+
# These will only be applied when starting a FOSS
|
70
|
+
# master, as a pe master is just bounced.
|
71
|
+
# @option conf_opts [Hash] :__service_args__ A special setting of options
|
72
|
+
# for controlling how the puppet master service is
|
73
|
+
# handled. The only setting currently is
|
74
|
+
# :bypass_service_script, which if set true will
|
75
|
+
# force stopping and starting a webrick master
|
76
|
+
# using the start_puppet_from_source_* methods,
|
77
|
+
# even if it seems the host has passenger.
|
78
|
+
# This is needed in FOSS tests to initialize
|
79
|
+
# SSL.
|
80
|
+
# @param [File] testdir The temporary directory which will hold backup
|
81
|
+
# configuration, and other test artifacts.
|
82
|
+
#
|
83
|
+
# @param [Block] block The point of this method, yields so
|
84
|
+
# tests may be ran. After the block is finished
|
85
|
+
# puppet will revert to a previous state.
|
86
|
+
#
|
87
|
+
# @example A simple use case to ensure a master is running
|
88
|
+
# with_puppet_running_on( master ) do
|
89
|
+
# ...tests that require a master...
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# @example Fully utilizing the possiblities of config options
|
93
|
+
# with_puppet_running_on( master,
|
94
|
+
# :main => {:logdest => '/var/blah'},
|
95
|
+
# :master => {:masterlog => '/elswhere'},
|
96
|
+
# :agent => {:server => 'localhost'} ) do
|
97
|
+
#
|
98
|
+
# ...tests to be ran...
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
def with_puppet_running_on host, conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
|
102
|
+
raise(ArgumentError, "with_puppet_running_on's conf_opts must be a Hash. You provided a #{conf_opts.class}: '#{conf_opts}'") if !conf_opts.kind_of?(Hash)
|
103
|
+
cmdline_args = conf_opts[:__commandline_args__]
|
104
|
+
service_args = conf_opts[:__service_args__] || {}
|
105
|
+
conf_opts = conf_opts.reject { |k,v| [:__commandline_args__, :__service_args__].include?(k) }
|
106
|
+
|
107
|
+
curl_retries = host['master-start-curl-retries'] || options['master-start-curl-retries']
|
108
|
+
logger.debug "Setting curl retries to #{curl_retries}"
|
109
|
+
|
110
|
+
if options[:is_puppetserver]
|
111
|
+
confdir = host.puppet('master')['confdir']
|
112
|
+
vardir = host.puppet('master')['vardir']
|
113
|
+
|
114
|
+
if cmdline_args
|
115
|
+
split_args = cmdline_args.split()
|
116
|
+
|
117
|
+
split_args.each do |arg|
|
118
|
+
case arg
|
119
|
+
when /--confdir=(.*)/
|
120
|
+
confdir = $1
|
121
|
+
when /--vardir=(.*)/
|
122
|
+
vardir = $1
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
puppetserver_opts = { "jruby-puppet" => {
|
128
|
+
"master-conf-dir" => confdir,
|
129
|
+
"master-var-dir" => vardir,
|
130
|
+
}}
|
131
|
+
|
132
|
+
puppetserver_conf = File.join("#{host['puppetserver-confdir']}", "puppetserver.conf")
|
133
|
+
modify_tk_config(host, puppetserver_conf, puppetserver_opts)
|
134
|
+
end
|
135
|
+
begin
|
136
|
+
backup_file = backup_the_file(host, host.puppet('master')['confdir'], testdir, 'puppet.conf')
|
137
|
+
lay_down_new_puppet_conf host, conf_opts, testdir
|
138
|
+
|
139
|
+
if host.use_service_scripts? && !service_args[:bypass_service_script]
|
140
|
+
bounce_service( host, host['puppetservice'], curl_retries )
|
141
|
+
else
|
142
|
+
puppet_master_started = start_puppet_from_source_on!( host, cmdline_args )
|
143
|
+
end
|
144
|
+
|
145
|
+
yield self if block_given?
|
146
|
+
|
147
|
+
rescue Beaker::DSL::Assertions, Minitest::Assertion => early_assertion
|
148
|
+
fail_test(early_assertion)
|
149
|
+
rescue Exception => early_exception
|
150
|
+
original_exception = RuntimeError.new("PuppetAcceptance::DSL::Helpers.with_puppet_running_on failed (check backtrace for location) because: #{early_exception}\n#{early_exception.backtrace.join("\n")}\n")
|
151
|
+
raise(original_exception)
|
152
|
+
|
153
|
+
ensure
|
154
|
+
begin
|
155
|
+
|
156
|
+
if host.use_service_scripts? && !service_args[:bypass_service_script]
|
157
|
+
restore_puppet_conf_from_backup( host, backup_file )
|
158
|
+
bounce_service( host, host['puppetservice'], curl_retries )
|
159
|
+
else
|
160
|
+
if puppet_master_started
|
161
|
+
stop_puppet_from_source_on( host )
|
162
|
+
else
|
163
|
+
dump_puppet_log(host)
|
164
|
+
end
|
165
|
+
restore_puppet_conf_from_backup( host, backup_file )
|
166
|
+
end
|
167
|
+
|
168
|
+
rescue Exception => teardown_exception
|
169
|
+
begin
|
170
|
+
if !host.is_pe?
|
171
|
+
dump_puppet_log(host)
|
172
|
+
end
|
173
|
+
rescue Exception => dumping_exception
|
174
|
+
logger.error("Raised during attempt to dump puppet logs: #{dumping_exception}")
|
175
|
+
end
|
176
|
+
|
177
|
+
if original_exception
|
178
|
+
logger.error("Raised during attempt to teardown with_puppet_running_on: #{teardown_exception}\n---\n")
|
179
|
+
raise original_exception
|
180
|
+
else
|
181
|
+
raise teardown_exception
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Test Puppet running in a certain run mode with specific options,
|
188
|
+
# on the default host
|
189
|
+
# @see #with_puppet_running_on
|
190
|
+
def with_puppet_running conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
|
191
|
+
with_puppet_running_on(default, conf_opts, testdir, &block)
|
192
|
+
end
|
193
|
+
|
194
|
+
# @!visibility private
|
195
|
+
def restore_puppet_conf_from_backup( host, backup_file )
|
196
|
+
puppet_conf = host.puppet('master')['config']
|
197
|
+
|
198
|
+
if backup_file
|
199
|
+
host.exec( Command.new( "if [ -f '#{backup_file}' ]; then " +
|
200
|
+
"cat '#{backup_file}' > " +
|
201
|
+
"'#{puppet_conf}'; " +
|
202
|
+
"rm -f '#{backup_file}'; " +
|
203
|
+
"fi" ) )
|
204
|
+
else
|
205
|
+
host.exec( Command.new( "rm -f '#{puppet_conf}'" ))
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
# @!visibility private
|
211
|
+
def start_puppet_from_source_on! host, args = ''
|
212
|
+
host.exec( puppet( 'master', args ) )
|
213
|
+
|
214
|
+
logger.debug 'Waiting for the puppet master to start'
|
215
|
+
unless port_open_within?( host, 8140, 10 )
|
216
|
+
raise Beaker::DSL::FailTest, 'Puppet master did not start in a timely fashion'
|
217
|
+
end
|
218
|
+
logger.debug 'The puppet master has started'
|
219
|
+
return true
|
220
|
+
end
|
221
|
+
|
222
|
+
# @!visibility private
|
223
|
+
def stop_puppet_from_source_on( host )
|
224
|
+
pid = host.exec( Command.new('cat `puppet master --configprint pidfile`') ).stdout.chomp
|
225
|
+
host.exec( Command.new( "kill #{pid}" ) )
|
226
|
+
Timeout.timeout(10) do
|
227
|
+
while host.exec( Command.new( "kill -0 #{pid}"), :acceptable_exit_codes => [0,1] ).exit_code == 0 do
|
228
|
+
# until kill -0 finds no process and we know that puppet has finished cleaning up
|
229
|
+
sleep 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# @!visibility private
|
235
|
+
def dump_puppet_log(host)
|
236
|
+
syslogfile = case host['platform']
|
237
|
+
when /fedora|centos|el|redhat|scientific/ then '/var/log/messages'
|
238
|
+
when /ubuntu|debian|cumulus/ then '/var/log/syslog'
|
239
|
+
else return
|
240
|
+
end
|
241
|
+
|
242
|
+
logger.notify "\n*************************"
|
243
|
+
logger.notify "* Dumping master log *"
|
244
|
+
logger.notify "*************************"
|
245
|
+
host.exec( Command.new( "tail -n 100 #{syslogfile}" ), :acceptable_exit_codes => [0,1])
|
246
|
+
logger.notify "*************************\n"
|
247
|
+
end
|
248
|
+
|
249
|
+
# @!visibility private
|
250
|
+
def lay_down_new_puppet_conf( host, configuration_options, testdir )
|
251
|
+
puppetconf_main = host.puppet('master')['config']
|
252
|
+
puppetconf_filename = File.basename(puppetconf_main)
|
253
|
+
puppetconf_test = File.join(testdir, puppetconf_filename)
|
254
|
+
|
255
|
+
new_conf = puppet_conf_for( host, configuration_options )
|
256
|
+
create_remote_file host, puppetconf_test, new_conf.to_s
|
257
|
+
|
258
|
+
host.exec(
|
259
|
+
Command.new( "cat #{puppetconf_test} > #{puppetconf_main}" ),
|
260
|
+
:silent => true
|
261
|
+
)
|
262
|
+
host.exec( Command.new( "cat #{puppetconf_main}" ) )
|
263
|
+
end
|
264
|
+
|
265
|
+
# @!visibility private
|
266
|
+
def puppet_conf_for host, conf_opts
|
267
|
+
puppetconf = host.exec( Command.new( "cat #{host.puppet('master')['config']}" ) ).stdout
|
268
|
+
new_conf = IniFile.new( puppetconf ).merge( conf_opts )
|
269
|
+
|
270
|
+
new_conf
|
271
|
+
end
|
272
|
+
|
273
|
+
# @!visibility private
|
274
|
+
def bounce_service host, service, curl_retries = 120
|
275
|
+
if host.graceful_restarts?
|
276
|
+
apachectl_path = host.is_pe? ? "#{host['puppetsbindir']}/apache2ctl" : 'apache2ctl'
|
277
|
+
host.exec(Command.new("#{apachectl_path} graceful"))
|
278
|
+
else
|
279
|
+
host.exec puppet_resource('service', service, 'ensure=stopped')
|
280
|
+
host.exec puppet_resource('service', service, 'ensure=running')
|
281
|
+
end
|
282
|
+
curl_with_retries(" #{service} ", host, "https://localhost:8140", [35, 60], curl_retries)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Runs 'puppet apply' on a remote host, piping manifest through stdin
|
286
|
+
#
|
287
|
+
# @param [Host] host The host that this command should be run on
|
288
|
+
#
|
289
|
+
# @param [String] manifest The puppet manifest to apply
|
290
|
+
#
|
291
|
+
# @!macro common_opts
|
292
|
+
# @option opts [Boolean] :parseonly (false) If this key is true, the
|
293
|
+
# "--parseonly" command line parameter will
|
294
|
+
# be passed to the 'puppet apply' command.
|
295
|
+
#
|
296
|
+
# @option opts [Boolean] :trace (false) If this key exists in the Hash,
|
297
|
+
# the "--trace" command line parameter will be
|
298
|
+
# passed to the 'puppet apply' command.
|
299
|
+
#
|
300
|
+
# @option opts [Array<Integer>] :acceptable_exit_codes ([0]) The list of exit
|
301
|
+
# codes that will NOT raise an error when found upon
|
302
|
+
# command completion. If provided, these values will
|
303
|
+
# be combined with those used in :catch_failures and
|
304
|
+
# :expect_failures to create the full list of
|
305
|
+
# passing exit codes.
|
306
|
+
#
|
307
|
+
# @option opts [Hash] :environment Additional environment variables to be
|
308
|
+
# passed to the 'puppet apply' command
|
309
|
+
#
|
310
|
+
# @option opts [Boolean] :catch_failures (false) By default `puppet
|
311
|
+
# --apply` will exit with 0, which does not count
|
312
|
+
# as a test failure, even if there were errors or
|
313
|
+
# changes when applying the manifest. This option
|
314
|
+
# enables detailed exit codes and causes a test
|
315
|
+
# failure if `puppet --apply` indicates there was
|
316
|
+
# a failure during its execution.
|
317
|
+
#
|
318
|
+
# @option opts [Boolean] :catch_changes (false) This option enables
|
319
|
+
# detailed exit codes and causes a test failure
|
320
|
+
# if `puppet --apply` indicates that there were
|
321
|
+
# changes or failures during its execution.
|
322
|
+
#
|
323
|
+
# @option opts [Boolean] :expect_changes (false) This option enables
|
324
|
+
# detailed exit codes and causes a test failure
|
325
|
+
# if `puppet --apply` indicates that there were
|
326
|
+
# no resource changes during its execution.
|
327
|
+
#
|
328
|
+
# @option opts [Boolean] :expect_failures (false) This option enables
|
329
|
+
# detailed exit codes and causes a test failure
|
330
|
+
# if `puppet --apply` indicates there were no
|
331
|
+
# failure during its execution.
|
332
|
+
#
|
333
|
+
# @option opts [Boolean] :future_parser (false) This option enables
|
334
|
+
# the future parser option that is available
|
335
|
+
# from Puppet verion 3.2
|
336
|
+
# By default it will use the 'current' parser.
|
337
|
+
#
|
338
|
+
# @option opts [Boolean] :noop (false) If this option exists, the
|
339
|
+
# the "--noop" command line parameter will be
|
340
|
+
# passed to the 'puppet apply' command.
|
341
|
+
#
|
342
|
+
# @option opts [String] :modulepath The search path for modules, as
|
343
|
+
# a list of directories separated by the system
|
344
|
+
# path separator character. (The POSIX path separator
|
345
|
+
# is ‘:’, and the Windows path separator is ‘;’.)
|
346
|
+
#
|
347
|
+
# @option opts [String] :debug (false) If this option exists,
|
348
|
+
# the "--debug" command line parameter
|
349
|
+
# will be passed to the 'puppet apply' command.
|
350
|
+
#
|
351
|
+
# @param [Block] block This method will yield to a block of code passed
|
352
|
+
# by the caller; this can be used for additional
|
353
|
+
# validation, etc.
|
354
|
+
#
|
355
|
+
def apply_manifest_on(host, manifest, opts = {}, &block)
|
356
|
+
block_on host do | host |
|
357
|
+
on_options = {}
|
358
|
+
on_options[:acceptable_exit_codes] = Array(opts[:acceptable_exit_codes])
|
359
|
+
|
360
|
+
puppet_apply_opts = {}
|
361
|
+
if opts[:debug]
|
362
|
+
puppet_apply_opts[:debug] = nil
|
363
|
+
else
|
364
|
+
puppet_apply_opts[:verbose] = nil
|
365
|
+
end
|
366
|
+
puppet_apply_opts[:parseonly] = nil if opts[:parseonly]
|
367
|
+
puppet_apply_opts[:trace] = nil if opts[:trace]
|
368
|
+
puppet_apply_opts[:parser] = 'future' if opts[:future_parser]
|
369
|
+
puppet_apply_opts[:modulepath] = opts[:modulepath] if opts[:modulepath]
|
370
|
+
puppet_apply_opts[:noop] = nil if opts[:noop]
|
371
|
+
|
372
|
+
# From puppet help:
|
373
|
+
# "... an exit code of '2' means there were changes, an exit code of
|
374
|
+
# '4' means there were failures during the transaction, and an exit
|
375
|
+
# code of '6' means there were both changes and failures."
|
376
|
+
if [opts[:catch_changes],opts[:catch_failures],opts[:expect_failures],opts[:expect_changes]].compact.length > 1
|
377
|
+
raise(ArgumentError,
|
378
|
+
'Cannot specify more than one of `catch_failures`, ' +
|
379
|
+
'`catch_changes`, `expect_failures`, or `expect_changes` ' +
|
380
|
+
'for a single manifest')
|
381
|
+
end
|
382
|
+
|
383
|
+
if opts[:catch_changes]
|
384
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
385
|
+
|
386
|
+
# We're after idempotency so allow exit code 0 only.
|
387
|
+
on_options[:acceptable_exit_codes] |= [0]
|
388
|
+
elsif opts[:catch_failures]
|
389
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
390
|
+
|
391
|
+
# We're after only complete success so allow exit codes 0 and 2 only.
|
392
|
+
on_options[:acceptable_exit_codes] |= [0, 2]
|
393
|
+
elsif opts[:expect_failures]
|
394
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
395
|
+
|
396
|
+
# We're after failures specifically so allow exit codes 1, 4, and 6 only.
|
397
|
+
on_options[:acceptable_exit_codes] |= [1, 4, 6]
|
398
|
+
elsif opts[:expect_changes]
|
399
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
400
|
+
|
401
|
+
# We're after changes specifically so allow exit code 2 only.
|
402
|
+
on_options[:acceptable_exit_codes] |= [2]
|
403
|
+
else
|
404
|
+
# Either use the provided acceptable_exit_codes or default to [0]
|
405
|
+
on_options[:acceptable_exit_codes] |= [0]
|
406
|
+
end
|
407
|
+
|
408
|
+
# Not really thrilled with this implementation, might want to improve it
|
409
|
+
# later. Basically, there is a magic trick in the constructor of
|
410
|
+
# PuppetCommand which allows you to pass in a Hash for the last value in
|
411
|
+
# the *args Array; if you do so, it will be treated specially. So, here
|
412
|
+
# we check to see if our caller passed us a hash of environment variables
|
413
|
+
# that they want to set for the puppet command. If so, we set the final
|
414
|
+
# value of *args to a new hash with just one entry (the value of which
|
415
|
+
# is our environment variables hash)
|
416
|
+
if opts.has_key?(:environment)
|
417
|
+
puppet_apply_opts['ENV'] = opts[:environment]
|
418
|
+
end
|
419
|
+
|
420
|
+
file_path = host.tmpfile('apply_manifest.pp')
|
421
|
+
create_remote_file(host, file_path, manifest + "\n")
|
422
|
+
|
423
|
+
if host[:default_apply_opts].respond_to? :merge
|
424
|
+
puppet_apply_opts = host[:default_apply_opts].merge( puppet_apply_opts )
|
425
|
+
end
|
426
|
+
|
427
|
+
on host, puppet('apply', file_path, puppet_apply_opts), on_options, &block
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Runs 'puppet apply' on default host, piping manifest through stdin
|
432
|
+
# @see #apply_manifest_on
|
433
|
+
def apply_manifest(manifest, opts = {}, &block)
|
434
|
+
apply_manifest_on(default, manifest, opts, &block)
|
435
|
+
end
|
436
|
+
|
437
|
+
# @deprecated
|
438
|
+
def run_agent_on(host, arg='--no-daemonize --verbose --onetime --test',
|
439
|
+
options={}, &block)
|
440
|
+
block_on host do | host |
|
441
|
+
on host, puppet_agent(arg), options, &block
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
# This method using the puppet resource 'host' will setup host aliases
|
446
|
+
# and register the remove of host aliases via Beaker::TestCase#teardown
|
447
|
+
#
|
448
|
+
# A teardown step is also added to make sure unstubbing of the host is
|
449
|
+
# removed always.
|
450
|
+
#
|
451
|
+
# @param [Host, Array<Host>, String, Symbol] machine One or more hosts to act upon,
|
452
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
453
|
+
# @param ip_spec [Hash{String=>String}] a hash containing the host to ip
|
454
|
+
# mappings
|
455
|
+
# @example Stub puppetlabs.com on the master to 127.0.0.1
|
456
|
+
# stub_hosts_on(master, 'puppetlabs.com' => '127.0.0.1')
|
457
|
+
def stub_hosts_on(machine, ip_spec)
|
458
|
+
block_on machine do | host |
|
459
|
+
ip_spec.each do |address, ip|
|
460
|
+
logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
|
461
|
+
on( host, puppet('resource', 'host', address, 'ensure=present', "ip=#{ip}") )
|
462
|
+
end
|
463
|
+
|
464
|
+
teardown do
|
465
|
+
ip_spec.each do |address, ip|
|
466
|
+
logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
|
467
|
+
on( host, puppet('resource', 'host', address, 'ensure=absent') )
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
# This method accepts a block and using the puppet resource 'host' will
|
474
|
+
# setup host aliases before and after that block.
|
475
|
+
#
|
476
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
477
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
478
|
+
# @param ip_spec [Hash{String=>String}] a hash containing the host to ip
|
479
|
+
# mappings
|
480
|
+
# @example Stub puppetlabs.com on the master to 127.0.0.1
|
481
|
+
# with_host_stubbed_on(master, 'forgeapi.puppetlabs.com' => '127.0.0.1') do
|
482
|
+
# puppet( "module install puppetlabs-stdlib" )
|
483
|
+
# end
|
484
|
+
def with_host_stubbed_on(host, ip_spec, &block)
|
485
|
+
begin
|
486
|
+
block_on host do |host|
|
487
|
+
ip_spec.each_pair do |address, ip|
|
488
|
+
logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
|
489
|
+
on( host, puppet('resource', 'host', address, 'ensure=present', "ip=#{ip}") )
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
block.call
|
494
|
+
|
495
|
+
ensure
|
496
|
+
ip_spec.each do |address, ip|
|
497
|
+
logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
|
498
|
+
on( host, puppet('resource', 'host', address, 'ensure=absent') )
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# This method accepts a block and using the puppet resource 'host' will
|
504
|
+
# setup host aliases before and after that block on the default host
|
505
|
+
#
|
506
|
+
# @example Stub puppetlabs.com on the default host to 127.0.0.1
|
507
|
+
# stub_hosts('puppetlabs.com' => '127.0.0.1')
|
508
|
+
# @see #stub_hosts_on
|
509
|
+
def stub_hosts(ip_spec)
|
510
|
+
stub_hosts_on(default, ip_spec)
|
511
|
+
end
|
512
|
+
|
513
|
+
# This wraps the method `stub_hosts_on` and makes the stub specific to
|
514
|
+
# the forge alias.
|
515
|
+
#
|
516
|
+
# forge api v1 canonical source is forge.puppetlabs.com
|
517
|
+
# forge api v3 canonical source is forgeapi.puppetlabs.com
|
518
|
+
#
|
519
|
+
# @param machine [String] the host to perform the stub on
|
520
|
+
# @param forge_host [String] The URL to use as the forge alias, will default to using :forge_host in the
|
521
|
+
# global options hash
|
522
|
+
def stub_forge_on(machine, forge_host = nil)
|
523
|
+
#use global options hash
|
524
|
+
forge_host ||= options[:forge_host]
|
525
|
+
@forge_ip ||= Resolv.getaddress(forge_host)
|
526
|
+
block_on machine do | host |
|
527
|
+
stub_hosts_on(host, 'forge.puppetlabs.com' => @forge_ip)
|
528
|
+
stub_hosts_on(host, 'forgeapi.puppetlabs.com' => @forge_ip)
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
# This wraps the method `with_host_stubbed_on` and makes the stub specific to
|
533
|
+
# the forge alias.
|
534
|
+
#
|
535
|
+
# forge api v1 canonical source is forge.puppetlabs.com
|
536
|
+
# forge api v3 canonical source is forgeapi.puppetlabs.com
|
537
|
+
#
|
538
|
+
# @param host [String] the host to perform the stub on
|
539
|
+
# @param forge_host [String] The URL to use as the forge alias, will default to using :forge_host in the
|
540
|
+
# global options hash
|
541
|
+
def with_forge_stubbed_on( host, forge_host = nil, &block )
|
542
|
+
#use global options hash
|
543
|
+
forge_host ||= options[:forge_host]
|
544
|
+
@forge_ip ||= Resolv.getaddress(forge_host)
|
545
|
+
with_host_stubbed_on( host,
|
546
|
+
{'forge.puppetlabs.com' => @forge_ip,
|
547
|
+
'forgeapi.puppetlabs.com' => @forge_ip},
|
548
|
+
&block )
|
549
|
+
end
|
550
|
+
|
551
|
+
# This wraps `with_forge_stubbed_on` and provides it the default host
|
552
|
+
# @see with_forge_stubbed_on
|
553
|
+
def with_forge_stubbed( forge_host = nil, &block )
|
554
|
+
with_forge_stubbed_on( default, forge_host, &block )
|
555
|
+
end
|
556
|
+
|
557
|
+
# This wraps the method `stub_hosts` and makes the stub specific to
|
558
|
+
# the forge alias.
|
559
|
+
#
|
560
|
+
# @see #stub_forge_on
|
561
|
+
def stub_forge(forge_host = nil)
|
562
|
+
#use global options hash
|
563
|
+
forge_host ||= options[:forge_host]
|
564
|
+
stub_forge_on(default, forge_host)
|
565
|
+
end
|
566
|
+
|
567
|
+
def sleep_until_puppetdb_started(host)
|
568
|
+
curl_with_retries("start puppetdb", host, "http://localhost:8080", 0, 120)
|
569
|
+
curl_with_retries("start puppetdb (ssl)",
|
570
|
+
host, "https://#{host.node_name}:8081", [35, 60])
|
571
|
+
end
|
572
|
+
|
573
|
+
def sleep_until_puppetserver_started(host)
|
574
|
+
curl_with_retries("start puppetserver (ssl)",
|
575
|
+
host, "https://#{host.node_name}:8140", [35, 60])
|
576
|
+
end
|
577
|
+
|
578
|
+
def sleep_until_nc_started(host)
|
579
|
+
curl_with_retries("start nodeclassifier (ssl)",
|
580
|
+
host, "https://#{host.node_name}:4433", [35, 60])
|
581
|
+
end
|
582
|
+
#stops the puppet agent running on the host
|
583
|
+
# @param [Host, Array<Host>, String, Symbol] agent One or more hosts to act upon,
|
584
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
585
|
+
def stop_agent_on(agent)
|
586
|
+
block_on agent do | host |
|
587
|
+
vardir = agent.puppet['vardir']
|
588
|
+
agent_running = true
|
589
|
+
while agent_running
|
590
|
+
agent_running = agent.file_exist?("#{vardir}/state/agent_catalog_run.lock")
|
591
|
+
if agent_running
|
592
|
+
sleep 2
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
# The agent service is `pe-puppet` everywhere EXCEPT certain linux distros on PE 2.8
|
597
|
+
# In all the case that it is different, this init script will exist. So we can assume
|
598
|
+
# that if the script doesn't exist, we should just use `pe-puppet`
|
599
|
+
agent_service = 'pe-puppet-agent'
|
600
|
+
agent_service = 'pe-puppet' unless agent.file_exist?('/etc/init.d/pe-puppet-agent')
|
601
|
+
|
602
|
+
# Under a number of stupid circumstances, we can't stop the
|
603
|
+
# agent using puppet. This is usually because of issues with
|
604
|
+
# the init script or system on that particular configuration.
|
605
|
+
avoid_puppet_at_all_costs = false
|
606
|
+
avoid_puppet_at_all_costs ||= agent['platform'] =~ /el-4/
|
607
|
+
avoid_puppet_at_all_costs ||= agent['pe_ver'] && version_is_less(agent['pe_ver'], '3.2') && agent['platform'] =~ /sles/
|
608
|
+
|
609
|
+
if avoid_puppet_at_all_costs
|
610
|
+
# When upgrading, puppet is already stopped. On EL4, this causes an exit code of '1'
|
611
|
+
on agent, "/etc/init.d/#{agent_service} stop", :acceptable_exit_codes => [0, 1]
|
612
|
+
else
|
613
|
+
on agent, puppet_resource('service', agent_service, 'ensure=stopped')
|
614
|
+
end
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
#stops the puppet agent running on the default host
|
619
|
+
# @see #stop_agent_on
|
620
|
+
def stop_agent
|
621
|
+
stop_agent_on(default)
|
622
|
+
end
|
623
|
+
|
624
|
+
#wait for a given host to appear in the dashboard
|
625
|
+
def wait_for_host_in_dashboard(host)
|
626
|
+
hostname = host.node_name
|
627
|
+
retry_on(dashboard, "! curl --tlsv1 -k -I https://#{dashboard}/nodes/#{hostname} | grep '404 Not Found'")
|
628
|
+
end
|
629
|
+
|
630
|
+
# Ensure the host has requested a cert, then sign it
|
631
|
+
#
|
632
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
633
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
634
|
+
#
|
635
|
+
# @return nil
|
636
|
+
# @raise [FailTest] if process times out
|
637
|
+
def sign_certificate_for(host)
|
638
|
+
block_on host do | host |
|
639
|
+
if [master, dashboard, database].include? host
|
640
|
+
|
641
|
+
on host, puppet( 'agent -t' ), :acceptable_exit_codes => [0,1,2]
|
642
|
+
on master, puppet( "cert --allow-dns-alt-names sign #{host}" ), :acceptable_exit_codes => [0,24]
|
643
|
+
|
644
|
+
else
|
645
|
+
|
646
|
+
hostname = Regexp.escape host.node_name
|
647
|
+
|
648
|
+
last_sleep = 0
|
649
|
+
next_sleep = 1
|
650
|
+
(0..10).each do |i|
|
651
|
+
fail_test("Failed to sign cert for #{hostname}") if i == 10
|
652
|
+
|
653
|
+
on master, puppet("cert --sign --all --allow-dns-alt-names"), :acceptable_exit_codes => [0,24]
|
654
|
+
break if on(master, puppet("cert --list --all")).stdout =~ /\+ "?#{hostname}"?/
|
655
|
+
sleep next_sleep
|
656
|
+
(last_sleep, next_sleep) = next_sleep, last_sleep+next_sleep
|
657
|
+
end
|
658
|
+
|
659
|
+
end
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
#prompt the master to sign certs then check to confirm the cert for the default host is signed
|
664
|
+
#@see #sign_certificate_for
|
665
|
+
def sign_certificate
|
666
|
+
sign_certificate_for(default)
|
667
|
+
end
|
668
|
+
|
669
|
+
# Create a temp directory on remote host with a user. Default user
|
670
|
+
# is puppet master user.
|
671
|
+
#
|
672
|
+
# @param [Host] host A single remote host on which to create and adjust
|
673
|
+
# the ownership of a temp directory.
|
674
|
+
# @param [String] name A remote path prefix for the new temp
|
675
|
+
# directory. Default value is '/tmp/beaker'
|
676
|
+
# @param [String] user The name of user that should own the temp
|
677
|
+
# directory. If no username is specified, use `puppet master
|
678
|
+
# --configprint user` to obtain username from master. Raise RuntimeError
|
679
|
+
# if this puppet command returns a non-zero exit code.
|
680
|
+
#
|
681
|
+
# @return [String] Returns the name of the newly-created dir.
|
682
|
+
def create_tmpdir_for_user(host, name='/tmp/beaker', user=nil)
|
683
|
+
if not user
|
684
|
+
result = on host, puppet("master --configprint user")
|
685
|
+
if not result.exit_code == 0
|
686
|
+
raise "`puppet master --configprint` failed, check that puppet is installed on #{host} or explicitly pass in a user name."
|
687
|
+
end
|
688
|
+
user = result.stdout.strip
|
689
|
+
end
|
690
|
+
|
691
|
+
create_tmpdir_on(host, name, user)
|
692
|
+
|
693
|
+
end
|
694
|
+
|
695
|
+
end
|
696
|
+
end
|
697
|
+
end
|
698
|
+
end
|