beaker 2.46.0 → 2.47.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.
@@ -41,3 +41,9 @@ Do not output any logging for this command. Defaults to `false`.
41
41
  Specifies standard input to be provided to the command post execution. Defaults to `nil`.
42
42
 
43
43
  on host, "this command takes input", {:stdin => "hiya"}
44
+
45
+ ## [:run_in_parallel](runner/run_in_parallel.md)
46
+
47
+ Execute the command against all hosts in parallel
48
+
49
+ on hosts, puppet( 'agent -t' ), :run_in_parallel => true
@@ -0,0 +1,62 @@
1
+ # run_in_parallel global and command options
2
+
3
+ ## run_in_parallel global option
4
+ The run_in_parallel global option is an array with the following possible values: ['configure', 'install']
5
+ It defaults to an empty array []
6
+ It can be set in an options file, or overriden by the BEAKER_RUN_IN_PARALLEL environment variable
7
+ example:
8
+
9
+ export BEAKER_RUN_IN_PARALLEL=configure,install
10
+
11
+ Including 'configure' causes timesync to execute in parallel (if timesync=true for any host)
12
+
13
+ Including 'install' causes as much of the puppet install to happen in parallel as possible.
14
+
15
+ ## run_in_parallel command option
16
+ The run_in_parallel command option is a boolean value, specifying whether to execute each iteration (usually of hosts)
17
+ in parallel, or not. The block_on method is the primary method accepting the run_in_parallel command option,
18
+ however many methods that call into block_on respect it as well:
19
+ - on
20
+ - run_block_on
21
+ - block_on
22
+ - install_puppet_agent_on
23
+ - apply_manifest_on
24
+ - stop_agent_on
25
+ - execute_powershell_script_on
26
+
27
+ ## Using InParallel in your test scripts
28
+ In addition to the options, you can use InParallel within your test scripts as well.
29
+
30
+ Examples:
31
+ ```ruby
32
+ include InParallel
33
+
34
+ test_name('test_test')
35
+
36
+ # Example 1
37
+ hosts.each_in_parallel{ |host|
38
+ # Do something on each host
39
+ }
40
+
41
+ def some_method_call
42
+ return "some_method_call"
43
+ end
44
+
45
+ def some_method_call
46
+ return "some_other_method_call"
47
+ end
48
+
49
+ # Example 2
50
+ # Runs each method within the block in parallel in a forked process
51
+ run_in_parallel{
52
+ @result = some_method_call
53
+ @result_2 = some_other_method_call
54
+ }
55
+
56
+ # results in 'some_method_callsome_other_method_call'
57
+ puts @result + @result_2
58
+ ```
59
+
60
+ **_Note:_** While you can return a result from a forked process to an instance variable, any values assigned to local variables, or other changes to global state will not persist from the child process to the parent process.
61
+
62
+ Further documentation on the usage of [InParallel](http://github/puppetlabs/in-parallel/readme.md)
data/lib/beaker.rb CHANGED
@@ -29,7 +29,10 @@ module Beaker
29
29
  #
30
30
  # Common setup and testing steps
31
31
  #require 'beaker/steps'
32
- #
32
+
33
+ # InParallel, for executing in parallel
34
+ require 'in_parallel'
35
+
33
36
  # Shared methods and helpers
34
37
  require 'beaker/shared'
35
38
 
data/lib/beaker/cli.rb CHANGED
@@ -14,6 +14,7 @@ module Beaker
14
14
  @options_parser = Beaker::Options::Parser.new
15
15
  @options = @options_parser.parse_args
16
16
  @logger = Beaker::Logger.new(@options)
17
+ InParallel::InParallelExecutor.logger = @logger
17
18
  @options[:logger] = @logger
18
19
  @options[:timestamp] = @timestamp
19
20
  @options[:beaker_version] = Beaker::Version::STRING
data/lib/beaker/dsl.rb CHANGED
@@ -56,8 +56,8 @@ module Beaker
56
56
  # require 'spec_helper'
57
57
  #
58
58
  # describe 'A Test With RSpec' do
59
- # let(:hosts) { Host.new('blah', 'blah', 'not helpful' }
60
- # let(:logger) { Where.is('the', 'rspec', 'logger') }
59
+ # let(:hosts) { Host.new('blah', 'blah', 'not helpful') }
60
+ # let(:logger) { Where.is('the', 'rspec', 'logger') }
61
61
  #
62
62
  # after do
63
63
  # on master, puppet('resource mything ensure=absent')
@@ -24,7 +24,7 @@ module Beaker
24
24
  # @option opts [Hash{String=>String}] :environment ({}) These will be
25
25
  # treated as extra environment variables that should be set before
26
26
  # running the command.
27
- #
27
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
28
28
 
29
29
  # The primary method for executing commands *on* some set of hosts.
30
30
  #
@@ -60,7 +60,7 @@ module Beaker
60
60
  # @return [Result] An object representing the outcome of *command*.
61
61
  # @raise [FailTest] Raises an exception if *command* obviously fails.
62
62
  def on(host, command, opts = {}, &block)
63
- block_on host do | host |
63
+ block_on host, opts do | host |
64
64
  if command.is_a? String
65
65
  cmd_opts = {}
66
66
  #add any additional environment variables to the command
@@ -280,10 +280,12 @@ module Beaker
280
280
  # that responds like
281
281
  # {Beaker::Host#do_scp_from}.
282
282
  # @param [String] powershell_script A string describing a set of powershell actions
283
+ # @param [Hash{Symbol=>String}] opts Options to alter execution.
284
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
283
285
  #
284
286
  # @return [Result] Returns the result of the powershell command execution
285
287
  def execute_powershell_script_on(hosts, powershell_script, opts = {})
286
- block_on hosts do |host|
288
+ block_on hosts, opts do |host|
287
289
  script_path = "beaker_powershell_script_#{Time.now.to_i}.ps1"
288
290
  create_remote_file(host, script_path, powershell_script, opts)
289
291
  native_path = script_path.gsub(/\//, "\\")
@@ -397,6 +397,7 @@ module Beaker
397
397
  # @option opts [String] :debug (false) If this option exists,
398
398
  # the "--debug" command line parameter
399
399
  # will be passed to the 'puppet apply' command.
400
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
400
401
  #
401
402
  # @param [Block] block This method will yield to a block of code passed
402
403
  # by the caller; this can be used for additional
@@ -405,7 +406,7 @@ module Beaker
405
406
  # @return [Array<Result>, Result, nil] An array of results, a result object,
406
407
  # or nil. Check {#run_block_on} for more details on this.
407
408
  def apply_manifest_on(host, manifest, opts = {}, &block)
408
- block_on host do | host |
409
+ block_on host, opts do | host |
409
410
  on_options = {}
410
411
  on_options[:acceptable_exit_codes] = Array(opts[:acceptable_exit_codes])
411
412
 
@@ -658,12 +659,14 @@ module Beaker
658
659
  #stops the puppet agent running on the host
659
660
  # @param [Host, Array<Host>, String, Symbol] agent One or more hosts to act upon,
660
661
  # or a role (String or Symbol) that identifies one or more hosts.
661
- def stop_agent_on(agent)
662
- block_on agent do | host |
663
- vardir = agent.puppet_configprint['vardir']
662
+ # @param [Hash{Symbol=>String}] opts Options to alter execution.
663
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
664
+ def stop_agent_on(agent, opts = {})
665
+ block_on agent, opts do | host |
666
+ vardir = host.puppet_configprint['vardir']
664
667
  agent_running = true
665
668
  while agent_running
666
- agent_running = agent.file_exist?("#{vardir}/state/agent_catalog_run.lock")
669
+ agent_running = host.file_exist?("#{vardir}/state/agent_catalog_run.lock")
667
670
  if agent_running
668
671
  sleep 2
669
672
  end
@@ -671,26 +674,26 @@ module Beaker
671
674
 
672
675
  # In 4.0 this was changed to just be `puppet`
673
676
  agent_service = 'puppet'
674
- if !aio_version?(agent)
677
+ if !aio_version?(host)
675
678
  # The agent service is `pe-puppet` everywhere EXCEPT certain linux distros on PE 2.8
676
679
  # In all the case that it is different, this init script will exist. So we can assume
677
680
  # that if the script doesn't exist, we should just use `pe-puppet`
678
681
  agent_service = 'pe-puppet-agent'
679
- agent_service = 'pe-puppet' unless agent.file_exist?('/etc/init.d/pe-puppet-agent')
682
+ agent_service = 'pe-puppet' unless host.file_exist?('/etc/init.d/pe-puppet-agent')
680
683
  end
681
684
 
682
685
  # Under a number of stupid circumstances, we can't stop the
683
686
  # agent using puppet. This is usually because of issues with
684
687
  # the init script or system on that particular configuration.
685
688
  avoid_puppet_at_all_costs = false
686
- avoid_puppet_at_all_costs ||= agent['platform'] =~ /el-4/
687
- avoid_puppet_at_all_costs ||= agent['pe_ver'] && version_is_less(agent['pe_ver'], '3.2') && agent['platform'] =~ /sles/
689
+ avoid_puppet_at_all_costs ||= host['platform'] =~ /el-4/
690
+ avoid_puppet_at_all_costs ||= host['pe_ver'] && version_is_less(host['pe_ver'], '3.2') && host['platform'] =~ /sles/
688
691
 
689
692
  if avoid_puppet_at_all_costs
690
693
  # When upgrading, puppet is already stopped. On EL4, this causes an exit code of '1'
691
- on agent, "/etc/init.d/#{agent_service} stop", :acceptable_exit_codes => [0, 1]
694
+ on host, "/etc/init.d/#{agent_service} stop", :acceptable_exit_codes => [0, 1]
692
695
  else
693
- on agent, puppet_resource('service', agent_service, 'ensure=stopped')
696
+ on host, puppet_resource('service', agent_service, 'ensure=stopped')
694
697
  end
695
698
  end
696
699
  end
@@ -714,35 +717,52 @@ module Beaker
714
717
 
715
718
  # Ensure the host has requested a cert, then sign it
716
719
  #
717
- # @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
718
- # or a role (String or Symbol) that identifies one or more hosts.
720
+ # @param [Host, Array<Host>, String, Symbol] host One or more hosts, or a role (String or Symbol)
721
+ # that identifies one or more hosts to validate certificate signing.
722
+ # No argument, or an empty array means no validation of success
723
+ # for specific hosts will be performed. This will always execute
724
+ # 'cert --sign --all --allow-dns-alt-names' even for a single host.
719
725
  #
720
726
  # @return nil
721
727
  # @raise [FailTest] if process times out
722
- def sign_certificate_for(host)
723
- block_on host do | host |
724
- if [master, dashboard, database].include? host
728
+ def sign_certificate_for(host = [])
729
+ hostnames = []
730
+ hosts = host.is_a?(Array) ? host : [host]
731
+ hosts.each{ |current_host|
732
+ if [master, dashboard, database].include? current_host
725
733
 
726
- on host, puppet( 'agent -t' ), :acceptable_exit_codes => [0,1,2]
727
- on master, puppet( "cert --allow-dns-alt-names sign #{host}" ), :acceptable_exit_codes => [0,24]
734
+ on current_host, puppet( 'agent -t' ), :acceptable_exit_codes => [0,1,2]
735
+ on master, puppet( "cert --allow-dns-alt-names sign #{current_host}" ), :acceptable_exit_codes => [0,24]
728
736
 
729
737
  else
730
-
731
- hostname = Regexp.escape host.node_name
732
-
733
- last_sleep = 0
734
- next_sleep = 1
735
- (0..10).each do |i|
736
- fail_test("Failed to sign cert for #{hostname}") if i == 10
737
-
738
- on master, puppet("cert --sign --all --allow-dns-alt-names"), :acceptable_exit_codes => [0,24]
739
- break if on(master, puppet("cert --list --all")).stdout =~ /\+ "?#{hostname}"?/
740
- sleep next_sleep
741
- (last_sleep, next_sleep) = next_sleep, last_sleep+next_sleep
738
+ hostnames << Regexp.escape( current_host.node_name )
739
+ end
740
+ }
741
+ if hostnames.size < 1
742
+ on master, puppet("cert --sign --all --allow-dns-alt-names"),
743
+ :acceptable_exit_codes => [0,24]
744
+ return
745
+ end
746
+ while hostnames.size > 0
747
+ last_sleep = 0
748
+ next_sleep = 1
749
+ (0..10).each do |i|
750
+ if i == 10
751
+ fail_test("Failed to sign cert for #{hostnames}")
752
+ hostnames.clear
753
+ end
754
+ on master, puppet("cert --sign --all --allow-dns-alt-names"), :acceptable_exit_codes => [0,24]
755
+ out = on(master, puppet("cert --list --all")).stdout
756
+ if hostnames.all? { |hostname| out =~ /\+ "?#{hostname}"?/ }
757
+ hostnames.clear
758
+ break
742
759
  end
743
760
 
761
+ sleep next_sleep
762
+ (last_sleep, next_sleep) = next_sleep, last_sleep+next_sleep
744
763
  end
745
764
  end
765
+ host
746
766
  end
747
767
 
748
768
  #prompt the master to sign certs then check to confirm the cert for the default host is signed
@@ -245,6 +245,7 @@ module Beaker
245
245
  # @option opts [String] :puppet_agent_version Version of puppet agent to download
246
246
  # @option opts [String] :mac_download_url Url to download msi pattern of %url%/puppet-%version%.msi
247
247
  # @option opts [String] :win_download_url Url to download dmg pattern of %url%/(puppet|hiera|facter)-%version%.msi
248
+ # @option opts [Boolean] :run_in_parallel Whether to install on all hosts in parallel. Defaults to false.
248
249
  #
249
250
  # @return nil
250
251
  # @raise [StandardError] When encountering an unsupported platform by default, or if gem cannot be found when default_action => 'gem_install'
@@ -257,9 +258,13 @@ module Beaker
257
258
  # backwards compatability
258
259
  opts[:puppet_agent_version] ||= opts[:version]
259
260
  install_puppet_agent_on(hosts, opts)
260
-
261
261
  else
262
- block_on hosts do |host|
262
+ # Use option specified in the method call, otherwise check whether the global
263
+ # run_in_parallel option includes install
264
+ run_in_parallel = opts[:run_in_parallel]
265
+ run_in_parallel = ((@options && @options[:run_in_parallel].is_a?(Array)) ?
266
+ @options[:run_in_parallel].include?('install') : false) if run_in_parallel.nil?
267
+ block_on hosts, { :run_in_parallel => run_in_parallel} do |host|
263
268
  if host['platform'] =~ /el-(5|6|7)/
264
269
  relver = $1
265
270
  install_puppet_from_rpm_on(host, opts.merge(:release => relver, :family => 'el'))
@@ -325,6 +330,7 @@ module Beaker
325
330
  # @option opts [String] :mac_download_url Url to download msi pattern of %url%/puppet-agent-%version%.msi
326
331
  # @option opts [String] :win_download_url Url to download dmg pattern of %url%/puppet-agent-%version%.msi
327
332
  # @option opts [String] :puppet_collection Defaults to 'pc1'
333
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
328
334
  #
329
335
  # @return nil
330
336
  # @raise [StandardError] When encountering an unsupported platform by default, or if gem cannot be found when default_action => 'gem_install'
@@ -334,7 +340,10 @@ module Beaker
334
340
  opts[:puppet_collection] ||= 'pc1' #hi! i'm case sensitive! be careful!
335
341
  opts[:puppet_agent_version] ||= opts[:version] #backwards compatability with old parameter name
336
342
 
337
- block_on hosts do |host|
343
+ run_in_parallel = opts[:run_in_parallel]
344
+ run_in_parallel = ((@options && @options[:run_in_parallel].is_a?(Array)) ?
345
+ @options[:run_in_parallel].include?('install') : false) if run_in_parallel.nil?
346
+ block_on hosts, { :run_in_parallel => run_in_parallel } do |host|
338
347
  add_role(host, 'aio') #we are installing agent, so we want aio role
339
348
  package_name = nil
340
349
  case host['platform']
@@ -379,6 +388,7 @@ module Beaker
379
388
  # @param [Hash{Symbol=>String}] opts
380
389
  # @option opts [Hash{String=>String}] :main configure the main section of puppet.conf
381
390
  # @option opts [Hash{String=>String}] :agent configure the agent section of puppet.conf
391
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
382
392
  #
383
393
  # @example will configure /etc/puppet.conf on the puppet master.
384
394
  # config = {
@@ -407,7 +417,7 @@ module Beaker
407
417
  puppet_conf_text << "\n"
408
418
  end
409
419
  logger.debug( "setting config '#{puppet_conf_text}' on hosts #{hosts}" )
410
- block_on hosts do |host|
420
+ block_on hosts, opts do |host|
411
421
  puppet_conf_path = host.puppet['config']
412
422
  create_remote_file(host, puppet_conf_path, puppet_conf_text)
413
423
  end
@@ -16,7 +16,7 @@ module Beaker
16
16
  # The directories in the module directory that will not be scp-ed to the test system when using
17
17
  # `copy_module_to`
18
18
  PUPPET_MODULE_INSTALL_IGNORE = ['.bundle', '.git', '.idea', '.vagrant', '.vendor', 'vendor', 'acceptance',
19
- 'bundle', 'spec', 'tests', 'log']
19
+ 'bundle', 'spec', 'tests', 'log', '.svn', 'junit', 'pkg', 'example']
20
20
 
21
21
  # Install the desired module on all hosts using either the PMT or a
22
22
  # staging forge
@@ -17,11 +17,13 @@ module Beaker
17
17
  # as a String that can be used to search for
18
18
  # a set of Hosts, or a {Host}
19
19
  # or Array<{Host}> to run the block against
20
+ # @param [Hash{Symbol=>String}] opts Options to alter execution.
21
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
20
22
  # @param [Block] block This method will yield to a block of code passed by the caller
21
23
  #
22
24
  # @return [Array<Result>, Result, nil] An array of results, a result object, or nil.
23
25
  # Check {#run_block_on} for more details on this.
24
- def block_on hosts_or_filter, &block
26
+ def block_on hosts_or_filter, opts={}, &block
25
27
  block_hosts = nil
26
28
  if defined? hosts
27
29
  block_hosts = hosts
@@ -32,7 +34,7 @@ module Beaker
32
34
  else
33
35
  block_hosts = hosts_or_filter
34
36
  end
35
- run_block_on block_hosts, filter, &block
37
+ run_block_on block_hosts, filter, opts, &block
36
38
  end
37
39
 
38
40
  end
@@ -52,7 +52,7 @@ module Beaker
52
52
  when host['platform'] =~ /sles-/
53
53
  ntp_command = "sntp #{ntp_server}"
54
54
  else
55
- ntp_command = "ntpdate -t 20 #{ntp_server}"
55
+ ntp_command = "ntpdate -u -t 20 #{ntp_server}"
56
56
  end
57
57
  success=false
58
58
  try = 0
@@ -71,6 +71,7 @@ module Beaker
71
71
  end
72
72
  end
73
73
  end
74
+ nil
74
75
  rescue => e
75
76
  report_and_raise(logger, e, "timesync (--ntp)")
76
77
  end
@@ -101,9 +101,10 @@ module Beaker
101
101
 
102
102
  #Default configuration steps to be run for a given hypervisor. Any additional configuration to be done
103
103
  #to the provided SUT for test execution to be successful.
104
- def configure
104
+ def configure(opts = {})
105
105
  return unless @options[:configure]
106
- block_on @hosts do |host|
106
+ block_on @hosts, { :run_in_parallel => (@options && @options[:run_in_parallel].is_a?(Array)) ?
107
+ @options[:run_in_parallel].include?('configure') : false} do |host|
107
108
  if host[:timesync]
108
109
  timesync(host, @options)
109
110
  end
@@ -36,6 +36,7 @@ module Beaker
36
36
  :vbguest_plugin => ['BEAKER_VB_GUEST_PLUGIN', 'BEAKER_vb_guest_plugin'],
37
37
  :tag_includes => ['BEAKER_TAG'],
38
38
  :tag_excludes => ['BEAKER_EXCLUDE_TAG'],
39
+ :run_in_parallel => ['BEAKER_RUN_IN_PARALLEL'],
39
40
  }
40
41
 
41
42
  # Select all environment variables whose name matches provided regex
@@ -86,6 +87,9 @@ module Beaker
86
87
 
87
88
  found_env_vars[:type] = type
88
89
  end
90
+ if found_env_vars[:run_in_parallel]
91
+ found_env_vars[:run_in_parallel] = found_env_vars[:run_in_parallel].split(',')
92
+ end
89
93
 
90
94
  found_env_vars[:pe_version_file_win] = found_env_vars[:pe_version_file]
91
95
  found_env_vars
@@ -188,6 +192,7 @@ module Beaker
188
192
  :nodeclassifier_port => 4433,
189
193
  :cache_files_locally => true, # TODO change to false in next major version
190
194
  :aws_keyname_modifier => rand(10 ** 10).to_s.rjust(10,'0'), # 10 digit random number string
195
+ :run_in_parallel => [],
191
196
  :ssh => {
192
197
  :config => false,
193
198
  :paranoid => false,
@@ -72,6 +72,8 @@ module Beaker
72
72
  #
73
73
  # @param [Array<Host>, Host] hosts The host or hosts to run the provided block against
74
74
  # @param [String, Symbol] filter Optional filter to apply to provided hosts - limits by name or role
75
+ # @param [Hash{Symbol=>String}] opts
76
+ # @option opts [Boolean] :run_in_parallel Whether to run on each host in parallel.
75
77
  # @param [Block] block This method will yield to a block of code passed by the caller
76
78
  #
77
79
  # @todo (beaker3.0:BKR-571): simplify return types to Array<Result> only
@@ -82,7 +84,7 @@ module Beaker
82
84
  # Else, a result object is returned. If filtering makes it such that only
83
85
  # one host is left, then it's passed as a host object (not in an array),
84
86
  # and thus a result object is returned.
85
- def run_block_on hosts = [], filter = nil, &block
87
+ def run_block_on hosts = [], filter = nil, opts = {}, &block
86
88
  result = nil
87
89
  block_hosts = hosts #the hosts to apply the block to after any filtering
88
90
  if filter
@@ -100,8 +102,18 @@ module Beaker
100
102
  end
101
103
  if block_hosts.is_a? Array
102
104
  if block_hosts.length > 0
103
- result = block_hosts.map do |h|
104
- run_block_on h, nil, &block
105
+ if opts[:run_in_parallel]
106
+ # Pass caller[1] - the line that called block_on - for logging purposes.
107
+ result = block_hosts.map.each_in_parallel(caller[1]) do |h|
108
+ run_block_on h, &block
109
+ end
110
+ hosts.each{|host| host.close}# For some reason, I have to close the SSH connection
111
+ # after spawning a process and running commands on a host,
112
+ # or else it gets into a broken state for the next call.
113
+ else
114
+ result = block_hosts.map do |h|
115
+ run_block_on h, &block
116
+ end
105
117
  end
106
118
  else
107
119
  # there are no matching hosts to execute against