beaker 2.7.1 → 2.8.0

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