beaker 2.14.1 → 2.15.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 (43) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +286 -27
  3. data/beaker.gemspec +1 -0
  4. data/lib/beaker/answers.rb +1 -1
  5. data/lib/beaker/answers/version40.rb +7 -5
  6. data/lib/beaker/cli.rb +2 -2
  7. data/lib/beaker/dsl/helpers/puppet_helpers.rb +24 -2
  8. data/lib/beaker/dsl/helpers/web_helpers.rb +3 -1
  9. data/lib/beaker/dsl/install_utils/foss_defaults.rb +11 -1
  10. data/lib/beaker/dsl/install_utils/foss_utils.rb +358 -248
  11. data/lib/beaker/dsl/install_utils/module_utils.rb +1 -7
  12. data/lib/beaker/dsl/install_utils/pe_defaults.rb +1 -1
  13. data/lib/beaker/dsl/install_utils/pe_utils.rb +63 -9
  14. data/lib/beaker/dsl/install_utils/puppet_utils.rb +40 -0
  15. data/lib/beaker/dsl/structure.rb +35 -0
  16. data/lib/beaker/host/pswindows/exec.rb +9 -0
  17. data/lib/beaker/host/unix/exec.rb +10 -1
  18. data/lib/beaker/host/unix/pkg.rb +0 -2
  19. data/lib/beaker/host_prebuilt_steps.rb +2 -2
  20. data/lib/beaker/hypervisor/aws_sdk.rb +1 -1
  21. data/lib/beaker/options/command_line_parser.rb +7 -0
  22. data/lib/beaker/options/parser.rb +28 -8
  23. data/lib/beaker/options/presets.rb +4 -3
  24. data/lib/beaker/shared/semvar.rb +23 -2
  25. data/lib/beaker/test_suite.rb +2 -2
  26. data/lib/beaker/version.rb +1 -1
  27. data/spec/beaker/cli_spec.rb +35 -34
  28. data/spec/beaker/dsl/helpers/puppet_helpers_spec.rb +78 -7
  29. data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +126 -17
  30. data/spec/beaker/dsl/install_utils/module_utils_spec.rb +2 -2
  31. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +106 -0
  32. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +73 -0
  33. data/spec/beaker/dsl/structure_spec.rb +67 -0
  34. data/spec/beaker/host/unix/exec_spec.rb +53 -0
  35. data/spec/beaker/host/windows/exec_spec.rb +53 -0
  36. data/spec/beaker/host_prebuilt_steps_spec.rb +13 -0
  37. data/spec/beaker/hypervisor/aws_sdk_spec.rb +65 -0
  38. data/spec/beaker/options/command_line_parser_spec.rb +2 -2
  39. data/spec/beaker/options/parser_spec.rb +126 -1
  40. data/spec/beaker/shared/semvar_spec.rb +43 -0
  41. data/spec/beaker/test_suite_spec.rb +21 -0
  42. data/spec/helpers.rb +4 -0
  43. metadata +19 -2
@@ -131,13 +131,7 @@ module Beaker
131
131
  scp_to host, source_path, target_module_dir, {:ignore => ignore_list}
132
132
  #rename to the selected module name, if not correct
133
133
  cur_path = File.join(target_module_dir, source_name)
134
- if (cur_path != target_path)
135
- if host.is_powershell?
136
- on host, "move /y #{cur_path} #{target_path}"
137
- else
138
- on host, "mv #{cur_path} #{target_path}"
139
- end
140
- end
134
+ host.mv cur_path, target_path unless cur_path == target_path
141
135
  when 'rsync'
142
136
  logger.debug "Using rsync to transfer #{source_path} to #{target_path}"
143
137
  rsync_to host, source_path, target_path, {:ignore => ignore_list}
@@ -110,7 +110,7 @@ module Beaker
110
110
  # @param [Host] host A single host to act upon
111
111
  # @param [String] platform The platform type of this host, one of windows, freebsd, mac & unix
112
112
  def remove_platform_pe_defaults(host, platform)
113
- FOSS_DEFAULTS[platform].each_pair do |key, val|
113
+ PE_DEFAULTS[platform].each_pair do |key, val|
114
114
  host.delete(key)
115
115
  end
116
116
  host['group'] = nil
@@ -26,7 +26,7 @@ module Beaker
26
26
  # or a role (String or Symbol) that identifies one or more hosts.
27
27
  def configure_pe_defaults_on( hosts )
28
28
  block_on hosts do |host|
29
- if host['type'] && host['type'] =~ /aio/
29
+ if (host[:pe_ver] && (not version_is_less(host[:pe_ver], '4.0'))) or (host['type'] && host['type'] =~ /aio/)
30
30
  # add pe defaults to host
31
31
  add_aio_defaults_on(host)
32
32
  else
@@ -361,9 +361,18 @@ module Beaker
361
361
  pre30master = version_is_less(opts[:pe_ver] || master['pe_ver'], '3.0')
362
362
  end
363
363
 
364
+ pe_versions = ( [] << opts['pe_ver'] << hosts.map{ |host| host['pe_ver'] } ).flatten.compact
365
+ agent_only_check_needed = version_is_less('3.99', max_version(pe_versions, '3.8'))
366
+ if agent_only_check_needed
367
+ hosts_agent_only, hosts_not_agent_only = create_agent_specified_arrays(hosts)
368
+ else
369
+ hosts_agent_only, hosts_not_agent_only = [], hosts.dup
370
+ end
371
+
364
372
  # Set PE distribution for all the hosts, create working dir
365
373
  use_all_tar = ENV['PE_USE_ALL_TAR'] == 'true'
366
374
  hosts.each do |host|
375
+ next if agent_only_check_needed && hosts_agent_only.include?(host)
367
376
  host['pe_installer'] ||= 'puppet-enterprise-installer'
368
377
  if host['platform'] !~ /windows|osx/
369
378
  platform = use_all_tar ? 'all' : host['platform']
@@ -395,7 +404,7 @@ module Beaker
395
404
  host['working_dir'] = host.tmpdir(Time.new.strftime("%Y-%m-%d_%H.%M.%S"))
396
405
  end
397
406
 
398
- fetch_pe(hosts, opts)
407
+ fetch_pe(hosts_not_agent_only, opts)
399
408
 
400
409
  install_hosts = hosts.dup
401
410
  unless masterless
@@ -404,7 +413,11 @@ module Beaker
404
413
  end
405
414
 
406
415
  install_hosts.each do |host|
407
- if host['platform'] =~ /windows/
416
+ if agent_only_check_needed && hosts_agent_only.include?(host)
417
+ host['type'] = 'aio'
418
+ install_puppet_agent_pe_promoted_repo_on(host, opts)
419
+ setup_defaults_and_config_helper_on(host, master, [0, 1])
420
+ elsif host['platform'] =~ /windows/
408
421
  on host, installer_cmd(host, opts)
409
422
  configure_pe_defaults_on(host)
410
423
  if not host.is_cygwin?
@@ -425,13 +438,8 @@ module Beaker
425
438
  elsif host['platform'] =~ /osx|eos/
426
439
  # If we're not frictionless, we need to run the OSX special-case
427
440
  on host, installer_cmd(host, opts)
428
- configure_pe_defaults_on(host)
429
- #set the certname and master
430
- on host, puppet("config set server #{master}")
431
- on host, puppet("config set certname #{host}")
432
- #run once to request cert
433
441
  acceptable_codes = host['platform'] =~ /osx/ ? [1] : [0, 1]
434
- on host, puppet_agent('-t'), :acceptable_exit_codes => acceptable_codes
442
+ setup_defaults_and_config_helper_on(host, master, acceptable_codes)
435
443
  else
436
444
  answers = Beaker::Answers.create(opts[:pe_ver] || host['pe_ver'], hosts, opts)
437
445
  create_remote_file host, "#{host['working_dir']}/answers", answers.answer_string(host)
@@ -483,6 +491,46 @@ module Beaker
483
491
  end
484
492
  end
485
493
 
494
+ # Builds the agent_only and not_agent_only arrays needed for installation.
495
+ #
496
+ # @param [Array<Host>] hosts hosts to split up into the arrays
497
+ #
498
+ # @note should only be called against versions 4.0+, as this method
499
+ # assumes AIO packages will be required.
500
+ #
501
+ # @api private
502
+ # @return [Array<Host>, Array<Host>]
503
+ # the array of hosts to do an agent_only install on and
504
+ # the array of hosts to do our usual install methods on
505
+ def create_agent_specified_arrays(hosts)
506
+ hosts_agent_only = []
507
+ hosts_not_agent_only = []
508
+ hosts.each do |host|
509
+ if host['roles'] && host['roles'].length == 1 && host['roles'][0] == 'agent'
510
+ hosts_agent_only << host
511
+ else
512
+ hosts_not_agent_only << host
513
+ end
514
+ end
515
+ return hosts_agent_only, hosts_not_agent_only
516
+ end
517
+
518
+ # Helper for setting up pe_defaults & setting up the cert on the host
519
+ # @param [Host] host host to setup
520
+ # @param [Host] master the master host, for setting up the relationship
521
+ # @param [Array<Fixnum>] acceptable_exit_codes The exit codes that we want to ignore
522
+ #
523
+ # @return nil
524
+ # @api private
525
+ def setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes=nil)
526
+ configure_pe_defaults_on(host)
527
+ #set the certname and master
528
+ on host, puppet("config set server #{master}")
529
+ on host, puppet("config set certname #{host}")
530
+ #run once to request cert
531
+ on host, puppet_agent('-t'), :acceptable_exit_codes => acceptable_exit_codes
532
+ end
533
+
486
534
  #Install PE based on global hosts with global options
487
535
  #@see #install_pe_on
488
536
  def install_pe
@@ -494,6 +542,12 @@ module Beaker
494
542
  # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
495
543
  # or a role (String or Symbol) that identifies one or more hosts.
496
544
  # @!macro common_opts
545
+ # @option opts [String] :sha The sha of puppet-agent to install. Required for PE agent
546
+ # only hosts on 4.0+
547
+ # @option opts [String] :version Version of puppet-agent to install. Required for PE agent
548
+ # only hosts on 4.0+
549
+ # @option opts [String] :pe_ver The version of PE (will also use host['pe_ver']), defaults to '4.0'
550
+ #
497
551
  # @example
498
552
  # install_pe_on(hosts, {})
499
553
  #
@@ -29,6 +29,7 @@ module Beaker
29
29
  def add_puppet_paths_on(hosts)
30
30
  block_on hosts do | host |
31
31
  host.add_env_var('PATH', construct_puppet_path(host))
32
+ host.add_env_var('PATH', 'PATH') # don't destroy the path!
32
33
  end
33
34
  end
34
35
 
@@ -42,6 +43,45 @@ module Beaker
42
43
  end
43
44
  end
44
45
 
46
+ #Configure the provided hosts to be of the provided type (one of foss, aio, pe), if the host
47
+ #is already associated with a type then remove the previous settings for that type
48
+ # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
49
+ # or a role (String or Symbol) that identifies one or more hosts.
50
+ # @param [String] type One of 'aio', 'pe' or 'foss'
51
+ def configure_defaults_on( hosts, type )
52
+ block_on hosts do |host|
53
+
54
+ # check to see if the host already has a type associated with it
55
+ remove_defaults_on(host)
56
+
57
+ add_method = "add_#{type}_defaults_on"
58
+ if self.respond_to?(add_method, host)
59
+ self.send(add_method, host)
60
+ else
61
+ raise "cannot add defaults of type #{type} for host #{host.name} (#{add_method} not present)"
62
+ end
63
+ # add pathing env
64
+ add_puppet_paths_on(host)
65
+ end
66
+ end
67
+
68
+ #If the host is associated with a type remove all defaults and environment associated with that type.
69
+ # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
70
+ # or a role (String or Symbol) that identifies one or more hosts.
71
+ def remove_defaults_on( hosts )
72
+ block_on hosts do |host|
73
+ if host['type']
74
+ remove_puppet_paths_on(hosts)
75
+ remove_method = "remove_#{host['type']}_defaults_on"
76
+ if self.respond_to?(remove_method, host)
77
+ self.send(remove_method, host)
78
+ else
79
+ raise "cannot remove defaults of type #{host['type']} associated with host #{host.name} (#{remove_method} not present)"
80
+ end
81
+ end
82
+ end
83
+ end
84
+
45
85
  end
46
86
  end
47
87
  end
@@ -209,6 +209,41 @@ module Beaker
209
209
  end
210
210
  end
211
211
 
212
+ # Sets tags on the current {Beaker::TestCase}, and skips testing
213
+ # if necessary after checking this case's tags against the ones that are
214
+ # being included or excluded.
215
+ #
216
+ # @param [Array<String>] tags Tags to be assigned to the current test
217
+ #
218
+ # @return nil
219
+ # @api public
220
+ def tag(*tags)
221
+ metadata[:case] ||= {}
222
+ metadata[:case][:tags] = []
223
+ tags.each do |tag|
224
+ metadata[:case][:tags] << tag.downcase
225
+ end
226
+
227
+ @options[:tag_includes] ||= []
228
+ @options[:tag_excludes] ||= []
229
+
230
+ tags_needed_to_include_this_test = []
231
+ @options[:tag_includes].each do |tag_to_include|
232
+ tags_needed_to_include_this_test << tag_to_include \
233
+ unless metadata[:case][:tags].include?(tag_to_include)
234
+ end
235
+ skip_test "#{self.path} does not include necessary tag(s): #{tags_needed_to_include_this_test}" \
236
+ if tags_needed_to_include_this_test.length > 0
237
+
238
+ tags_to_remove_to_include_this_test = []
239
+ @options[:tag_excludes].each do |tag_to_exclude|
240
+ tags_to_remove_to_include_this_test << tag_to_exclude \
241
+ if metadata[:case][:tags].include?(tag_to_exclude)
242
+ end
243
+ skip_test "#{self.path} includes excluded tag(s): #{tags_to_remove_to_include_this_test}" \
244
+ if tags_to_remove_to_include_this_test.length > 0
245
+ end
246
+
212
247
  #Return a set of hosts that meet the given criteria
213
248
  # @param [Hash{Symbol,String=>String,Regexp,Array<String,Regexp>}]
214
249
  # criteria Specify the criteria with which a host should be
@@ -21,6 +21,15 @@ module PSWindows::Exec
21
21
  execute("del /s /q #{path}")
22
22
  end
23
23
 
24
+ # Move the origin to destination. The destination is removed prior to moving.
25
+ # @param [String] orig The origin path
26
+ # @param [String] dest the destination path
27
+ # @param [Boolean] rm Remove the destination prior to move
28
+ def mv(orig, dest, rm=true)
29
+ rm_rf dest unless !rm
30
+ execute("move /y #{orig} #{dest}")
31
+ end
32
+
24
33
  def path
25
34
  'c:/windows/system32;c:/windows'
26
35
  end
@@ -41,7 +41,16 @@ module Unix::Exec
41
41
  # Recursively remove the path provided
42
42
  # @param [String] path The path to remove
43
43
  def rm_rf path
44
- exec(Beaker::Command.new("rm -rf #{path}"))
44
+ execute("rm -rf #{path}")
45
+ end
46
+
47
+ # Move the origin to destination. The destination is removed prior to moving.
48
+ # @param [String] orig The origin path
49
+ # @param [String] dest the destination path
50
+ # @param [Boolean] rm Remove the destination prior to move
51
+ def mv orig, dest, rm=true
52
+ rm_rf dest unless !rm
53
+ execute("mv #{orig} #{dest}")
45
54
  end
46
55
 
47
56
  # Converts the provided environment file to a new shell script in /etc/profile.d, then sources that file.
@@ -61,8 +61,6 @@ module Unix::Pkg
61
61
  case self['platform']
62
62
  when /sles-/
63
63
  execute("zypper --non-interactive in #{name}", opts)
64
- when /el-4/
65
- @logger.debug("Package installation not supported on rhel4")
66
64
  when /cisco|fedora|centos|eos|el-/
67
65
  if version
68
66
  name = "#{name}-#{version}"
@@ -13,7 +13,7 @@ module Beaker
13
13
  SLEEPWAIT = 5
14
14
  TRIES = 5
15
15
  UNIX_PACKAGES = ['curl', 'ntpdate']
16
- FREEBSD_PACKAGES = ['curl']
16
+ FREEBSD_PACKAGES = ['curl', 'perl5']
17
17
  WINDOWS_PACKAGES = ['curl']
18
18
  PSWINDOWS_PACKAGES = []
19
19
  SLES10_PACKAGES = ['curl']
@@ -521,7 +521,7 @@ module Beaker
521
521
  host.exec(Command.new("stopsrc -g ssh"))
522
522
  host.exec(Command.new("startsrc -g ssh"))
523
523
  when /freebsd/
524
- host.echo_to_file('\nPermitUserEnvironment yes', '/etc/ssh/sshd_config')
524
+ host.exec(Command.new("sudo perl -pi -e 's/^#?PermitUserEnvironment no/PermitUserEnvironment yes/' /etc/ssh/sshd_config"), {:pty => true} )
525
525
  host.exec(Command.new("sudo /etc/rc.d/sshd restart"))
526
526
  end
527
527
 
@@ -473,7 +473,7 @@ module Beaker
473
473
  @hosts.each do |host|
474
474
  @logger.notify("aws-sdk: Populate DNS for #{host.name}")
475
475
  instance = host['instance']
476
- host['ip'] = instance.ip_address
476
+ host['ip'] = instance.ip_address ? instance.ip_address : instance.private_ip_address
477
477
  host['private_ip'] = instance.private_ip_address
478
478
  host['dns_name'] = instance.dns_name
479
479
  @logger.notify("aws-sdk: name: #{host.name} ip: #{host['ip']} private_ip: #{host['private_ip']} dns_name: #{instance.dns_name}")
@@ -223,6 +223,13 @@ module Beaker
223
223
  @cmd_options[:type] = type
224
224
  end
225
225
 
226
+ opts.on '--tag TAGS', 'Run the set of tests matching ALL of the provided single or comma separated list of tags' do |value|
227
+ @cmd_options[:tag_includes] = value
228
+ end
229
+
230
+ opts.on '--exclude-tag TAGS', 'Run the set of tests that do not contain ANY of the provided single or command separated list of tags' do |value|
231
+ @cmd_options[:tag_excludes] = value
232
+ end
226
233
  end
227
234
 
228
235
  end
@@ -170,18 +170,13 @@ module Beaker
170
170
  # Order of priority is as follows:
171
171
  # 1. environment variables are given top priority
172
172
  # 2. ARGV or provided arguments array
173
- # 3. host file options
174
- # 4. the 'CONFIG' section of the hosts file
175
- # 5. options file values
176
- # 6. default or preset values are given the lowest priority
173
+ # 3. the 'CONFIG' section of the hosts file
174
+ # 4. options file values
175
+ # 5. default or preset values are given the lowest priority
177
176
  #
178
177
  # @param [Array] args ARGV or a provided arguments array
179
178
  # @raise [ArgumentError] Raises error on bad input
180
179
  def parse_args(args = ARGV)
181
- # NOTE on argument precedence:
182
- # Will use env, then hosts/config file, then command line, then file options
183
-
184
-
185
180
  @options = @presets.presets
186
181
  cmd_line_options = @command_line_parser.parse(args)
187
182
  cmd_line_options[:command_line] = ([$0] + args).join(' ')
@@ -332,6 +327,8 @@ module Beaker
332
327
  end
333
328
  end
334
329
 
330
+ normalize_and_validate_tags()
331
+
335
332
  #set the default role
336
333
  set_default_host!(@options[:HOSTS])
337
334
 
@@ -345,6 +342,29 @@ module Beaker
345
342
 
346
343
  end
347
344
 
345
+ # Takes tag arguments given at this point, which are strings, converts
346
+ # them into the proper format (an array of strings) for Beaker use,
347
+ # and lastly, validates them.
348
+ #
349
+ # @return nil
350
+ # @api public
351
+ def normalize_and_validate_tags()
352
+ @options[:tag_includes] ||= ''
353
+ @options[:tag_excludes] ||= ''
354
+ @options[:tag_includes] = @options[:tag_includes].split(',') if @options[:tag_includes].respond_to?(:split)
355
+ @options[:tag_excludes] = @options[:tag_excludes].split(',') if @options[:tag_excludes].respond_to?(:split)
356
+ @options[:tag_includes].map!(&:downcase)
357
+ @options[:tag_excludes].map!(&:downcase)
358
+
359
+ #check that a tag is not both included & excluded sets
360
+ @options[:tag_includes].each do |included_tag|
361
+ @options[:tag_excludes].each do |excluded_tag|
362
+ parser_error "tag '#{included_tag}' cannot be in both the included and excluded tag sets" \
363
+ if included_tag == excluded_tag
364
+ end
365
+ end
366
+ end
367
+
348
368
  private
349
369
 
350
370
  # @api private
@@ -31,6 +31,8 @@ module Beaker
31
31
  :release_yum_repo_url => ['BEAKER_RELEASE_YUM_REPO', 'RELEASE_YUM_REPO'],
32
32
  :dev_builds_url => ['BEAKER_DEV_BUILDS_URL', 'DEV_BUILDS_URL'],
33
33
  :vbguest_plugin => ['BEAKER_VB_GUEST_PLUGIN', 'BEAKER_vb_guest_plugin'],
34
+ :tag_includes => ['BEAKER_TAG'],
35
+ :tag_excludes => ['BEAKER_EXCLUDE_TAG'],
34
36
  }
35
37
 
36
38
  # Select all environment variables whose name matches provided regex
@@ -142,6 +144,8 @@ module Beaker
142
144
  :log_sut_event => 'sut.log',
143
145
  :color => true,
144
146
  :dry_run => false,
147
+ :tag_includes => '',
148
+ :tag_excludes => '',
145
149
  :timeout => 300,
146
150
  :fail_mode => 'slow',
147
151
  :accept_all_exit_codes => false,
@@ -151,9 +155,6 @@ module Beaker
151
155
  :repo_proxy => false,
152
156
  :package_proxy => false,
153
157
  :add_el_extras => false,
154
- :release_apt_repo_url => "http://apt.puppetlabs.com",
155
- :release_yum_repo_url => "http://yum.puppetlabs.com",
156
- :dev_builds_url => "http://builds.delivery.puppetlabs.net",
157
158
  :epel_url => "http://mirrors.kernel.org/fedora-epel",
158
159
  :epel_arch => "i386",
159
160
  :epel_6_pkg => "epel-release-6-8.noarch.rpm",
@@ -14,9 +14,9 @@ module Beaker
14
14
  b_nums = b.split('-')[0].split('.')
15
15
  (0...a_nums.length).each do |i|
16
16
  if i < b_nums.length
17
- if a_nums[i] < b_nums[i]
17
+ if a_nums[i].to_i < b_nums[i].to_i
18
18
  return true
19
- elsif a_nums[i] > b_nums[i]
19
+ elsif a_nums[i].to_i > b_nums[i].to_i
20
20
  return false
21
21
  end
22
22
  else
@@ -35,6 +35,27 @@ module Beaker
35
35
  end
36
36
  return false
37
37
  end
38
+
39
+ # Gets the max semver version from a list of them
40
+ # @param [Array<String>] versions List of versions to get max from
41
+ # @param [String] default Default version if list is nil or empty
42
+ #
43
+ # @note nil values will be skipped
44
+ # @note versions parameter will be copied so that the original
45
+ # won't be tampered with
46
+ #
47
+ # @return [String, nil] the max string out of the versions list or the
48
+ # default value if the list is faulty, which can either be set or nil
49
+ def max_version(versions, default=nil)
50
+ return default if !versions || versions.empty?
51
+ versions_copy = versions.dup
52
+ highest = versions_copy.shift
53
+ versions_copy.each do |version|
54
+ next if !version
55
+ highest = version if version_is_less(highest, version)
56
+ end
57
+ highest
58
+ end
38
59
  end
39
60
  end
40
61
  end