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.
Files changed (53) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +121 -2
  3. data/lib/beaker/dsl.rb +2 -2
  4. data/lib/beaker/dsl/helpers.rb +13 -1429
  5. data/lib/beaker/dsl/helpers/facter_helpers.rb +48 -0
  6. data/lib/beaker/dsl/helpers/hiera_helpers.rb +71 -0
  7. data/lib/beaker/dsl/helpers/host_helpers.rb +506 -0
  8. data/lib/beaker/dsl/helpers/puppet_helpers.rb +698 -0
  9. data/lib/beaker/dsl/helpers/tk_helpers.rb +101 -0
  10. data/lib/beaker/dsl/helpers/web_helpers.rb +115 -0
  11. data/lib/beaker/dsl/install_utils.rb +8 -1570
  12. data/lib/beaker/dsl/install_utils/ezbake_utils.rb +256 -0
  13. data/lib/beaker/dsl/install_utils/module_utils.rb +237 -0
  14. data/lib/beaker/dsl/install_utils/pe_utils.rb +518 -0
  15. data/lib/beaker/dsl/install_utils/puppet_utils.rb +722 -0
  16. data/lib/beaker/dsl/outcomes.rb +0 -4
  17. data/lib/beaker/dsl/roles.rb +0 -3
  18. data/lib/beaker/dsl/structure.rb +127 -4
  19. data/lib/beaker/dsl/wrappers.rb +0 -4
  20. data/lib/beaker/host.rb +23 -0
  21. data/lib/beaker/host/unix/pkg.rb +4 -4
  22. data/lib/beaker/host_prebuilt_steps.rb +11 -5
  23. data/lib/beaker/hypervisor/vagrant.rb +1 -0
  24. data/lib/beaker/hypervisor/vmpooler.rb +38 -0
  25. data/lib/beaker/logger.rb +10 -4
  26. data/lib/beaker/network_manager.rb +5 -4
  27. data/lib/beaker/options/command_line_parser.rb +7 -0
  28. data/lib/beaker/shared.rb +2 -1
  29. data/lib/beaker/shared/semvar.rb +41 -0
  30. data/lib/beaker/test_suite.rb +20 -6
  31. data/lib/beaker/version.rb +1 -1
  32. data/spec/beaker/dsl/helpers/facter_helpers_spec.rb +59 -0
  33. data/spec/beaker/dsl/helpers/hiera_helpers_spec.rb +96 -0
  34. data/spec/beaker/dsl/helpers/host_helpers_spec.rb +413 -0
  35. data/spec/beaker/dsl/{helpers_spec.rb → helpers/puppet_helpers_spec.rb} +2 -611
  36. data/spec/beaker/dsl/helpers/tk_helpers_spec.rb +83 -0
  37. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +60 -0
  38. data/spec/beaker/dsl/install_utils/module_utils_spec.rb +241 -0
  39. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +475 -0
  40. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +523 -0
  41. data/spec/beaker/dsl/structure_spec.rb +108 -0
  42. data/spec/beaker/host_prebuilt_steps_spec.rb +44 -0
  43. data/spec/beaker/host_spec.rb +41 -0
  44. data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
  45. data/spec/beaker/logger_spec.rb +9 -2
  46. data/spec/beaker/network_manager_spec.rb +7 -1
  47. data/spec/beaker/options/command_line_parser_spec.rb +3 -2
  48. data/spec/beaker/shared/semvar_spec.rb +36 -0
  49. data/spec/beaker/test_suite_spec.rb +48 -0
  50. data/spec/mocks.rb +10 -0
  51. metadata +23 -5
  52. data/lib/beaker/dsl/ezbake_utils.rb +0 -259
  53. 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