beaker 2.18.0 → 2.19.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 (77) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +699 -2
  3. data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
  4. data/acceptance/pre_suite/puppet_gem/install.rb +1 -8
  5. data/acceptance/pre_suite/puppet_git/install.rb +6 -65
  6. data/acceptance/pre_suite/puppet_pkg/install.rb +1 -1
  7. data/acceptance/tests/foss_utils/clone_git_repo_on.rb +49 -0
  8. data/beaker.gemspec +2 -0
  9. data/lib/beaker/command.rb +1 -1
  10. data/lib/beaker/dsl/helpers/puppet_helpers.rb +8 -6
  11. data/lib/beaker/dsl/helpers/web_helpers.rb +2 -1
  12. data/lib/beaker/dsl/install_utils/aio_defaults.rb +0 -3
  13. data/lib/beaker/dsl/install_utils/foss_defaults.rb +19 -0
  14. data/lib/beaker/dsl/install_utils/foss_utils.rb +164 -67
  15. data/lib/beaker/dsl/install_utils/pe_defaults.rb +9 -11
  16. data/lib/beaker/dsl/install_utils/pe_utils.rb +48 -64
  17. data/lib/beaker/dsl/install_utils/puppet_utils.rb +43 -0
  18. data/lib/beaker/dsl/install_utils/windows_utils.rb +144 -0
  19. data/lib/beaker/dsl/roles.rb +20 -3
  20. data/lib/beaker/dsl/structure.rb +14 -3
  21. data/lib/beaker/host/freebsd/pkg.rb +18 -0
  22. data/lib/beaker/host/freebsd.rb +2 -0
  23. data/lib/beaker/host/unix/exec.rb +3 -3
  24. data/lib/beaker/host/unix/pkg.rb +37 -0
  25. data/lib/beaker/host/windows/exec.rb +3 -0
  26. data/lib/beaker/host.rb +38 -9
  27. data/lib/beaker/host_prebuilt_steps.rb +21 -11
  28. data/lib/beaker/hypervisor/aws_sdk.rb +22 -18
  29. data/lib/beaker/hypervisor/docker.rb +7 -0
  30. data/lib/beaker/hypervisor/openstack.rb +1 -0
  31. data/lib/beaker/hypervisor/vagrant_virtualbox.rb +10 -5
  32. data/lib/beaker/hypervisor/vmpooler.rb +4 -0
  33. data/lib/beaker/logger.rb +12 -1
  34. data/lib/beaker/options/command_line_parser.rb +9 -0
  35. data/lib/beaker/options/options_hash.rb +3 -296
  36. data/lib/beaker/options/parser.rb +12 -0
  37. data/lib/beaker/options/presets.rb +0 -1
  38. data/lib/beaker/platform.rb +3 -1
  39. data/lib/beaker/ssh_connection.rb +48 -23
  40. data/lib/beaker/test_case.rb +1 -1
  41. data/lib/beaker/version.rb +1 -1
  42. data/spec/beaker/dsl/helpers/puppet_helpers_spec.rb +0 -1
  43. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +10 -1
  44. data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +247 -49
  45. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +116 -26
  46. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +57 -0
  47. data/spec/beaker/dsl/install_utils/windows_utils_spec.rb +132 -0
  48. data/spec/beaker/dsl/roles_spec.rb +36 -5
  49. data/spec/beaker/dsl/structure_spec.rb +9 -2
  50. data/spec/beaker/host/unix/pkg_spec.rb +26 -6
  51. data/spec/beaker/host_prebuilt_steps_spec.rb +3 -2
  52. data/spec/beaker/host_spec.rb +24 -6
  53. data/spec/beaker/hypervisor/aixer_spec.rb +1 -1
  54. data/spec/beaker/hypervisor/aws_sdk_spec.rb +595 -58
  55. data/spec/beaker/hypervisor/docker_spec.rb +2 -1
  56. data/spec/beaker/hypervisor/solaris_spec.rb +1 -0
  57. data/spec/beaker/hypervisor/vagrant_spec.rb +20 -5
  58. data/spec/beaker/hypervisor/vagrant_virtualbox_spec.rb +1 -1
  59. data/spec/beaker/logger_spec.rb +39 -0
  60. data/spec/beaker/options/command_line_parser_spec.rb +2 -2
  61. data/spec/beaker/options/options_hash_spec.rb +1 -102
  62. data/spec/beaker/options/parser_spec.rb +19 -0
  63. data/spec/beaker/options/pe_version_scaper_spec.rb +11 -1
  64. data/spec/beaker/options/presets_spec.rb +8 -0
  65. data/spec/beaker/ssh_connection_spec.rb +39 -21
  66. data/spec/helpers.rb +9 -3
  67. data/spec/mocks.rb +2 -0
  68. metadata +35 -11
  69. data/lib/beaker/answers/version20.rb +0 -120
  70. data/lib/beaker/answers/version28.rb +0 -121
  71. data/lib/beaker/answers/version30.rb +0 -227
  72. data/lib/beaker/answers/version32.rb +0 -44
  73. data/lib/beaker/answers/version34.rb +0 -51
  74. data/lib/beaker/answers/version38.rb +0 -29
  75. data/lib/beaker/answers/version40.rb +0 -44
  76. data/lib/beaker/answers.rb +0 -143
  77. data/spec/beaker/answers_spec.rb +0 -547
@@ -1,6 +1,7 @@
1
- [ 'aio_defaults', 'pe_defaults', 'puppet_utils' ].each do |lib|
1
+ [ 'aio_defaults', 'pe_defaults', 'puppet_utils', 'windows_utils' ].each do |lib|
2
2
  require "beaker/dsl/install_utils/#{lib}"
3
3
  end
4
+ require "beaker-answers"
4
5
  module Beaker
5
6
  module DSL
6
7
  module InstallUtils
@@ -18,24 +19,7 @@ module Beaker
18
19
  include AIODefaults
19
20
  include PEDefaults
20
21
  include PuppetUtils
21
-
22
- # Set defaults and PATH for these hosts to be either pe or aio, have host['type'] == aio for aio settings, defaults
23
- # to pe.
24
- #
25
- # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
26
- # or a role (String or Symbol) that identifies one or more hosts.
27
- def configure_pe_defaults_on( hosts )
28
- block_on hosts do |host|
29
- if host[:pe_ver] && aio_version?(host) or (host['type'] && host['type'] =~ /aio/)
30
- add_aio_defaults_on(host)
31
- else
32
- add_pe_defaults_on(host)
33
- end
34
- # add pathing env
35
- add_puppet_paths_on(host)
36
- end
37
- end
38
-
22
+ include WindowsUtils
39
23
 
40
24
  # @!macro [new] common_opts
41
25
  # @param [Hash{Symbol=>String}] opts Options to alter execution.
@@ -58,6 +42,7 @@ module Beaker
58
42
  # running the command.
59
43
 
60
44
  #Sort array of hosts so that it has the correct order for PE installation based upon each host's role
45
+ #@param subset [Array<Host>] An array of hosts to sort, defaults to global 'hosts' object
61
46
  # @example
62
47
  # h = sorted_hosts
63
48
  #
@@ -68,12 +53,13 @@ module Beaker
68
53
  # Fourth: everything else
69
54
  #
70
55
  # @!visibility private
71
- def sorted_hosts
56
+ def sorted_hosts subset = hosts
72
57
  special_nodes = []
73
58
  [master, database, dashboard].uniq.each do |host|
74
- special_nodes << host if host != nil
59
+ special_nodes << host if host != nil && subset.include?(host)
75
60
  end
76
61
  real_agents = agents - special_nodes
62
+ real_agents = real_agents.delete_if{ |host| !subset.include?(host) }
77
63
  special_nodes + real_agents
78
64
  end
79
65
 
@@ -81,8 +67,6 @@ module Beaker
81
67
  # @param [Host] host The host that PE is to be installed on
82
68
  # For UNIX machines using the full PE installer, the host object must have the 'pe_installer' field set correctly.
83
69
  # @param [Hash{Symbol=>String}] opts The options
84
- # @option opts [String] :pe_ver_win Default PE version to install or upgrade to on Windows hosts
85
- # (Othersie uses individual Windows hosts pe_ver)
86
70
  # @option opts [String] :pe_ver Default PE version to install or upgrade to
87
71
  # (Otherwise uses individual hosts pe_ver)
88
72
  # @option opts [Boolean] :pe_debug (false) Should we run the installer in debug mode?
@@ -91,17 +75,9 @@ module Beaker
91
75
  # @api private
92
76
  def installer_cmd(host, opts)
93
77
  version = host['pe_ver'] || opts[:pe_ver]
94
- if host['platform'] =~ /windows/
95
- log_file = "#{File.basename(host['working_dir'])}.log"
96
- pe_debug = host[:pe_debug] || opts[:pe_debug] ? " && cat #{log_file}" : ''
97
- if host.is_cygwin?
98
- "cd #{host['working_dir']} && cmd /C 'start /w msiexec.exe /qn /L*V #{log_file} /i #{host['dist']}.msi PUPPET_MASTER_SERVER=#{master} PUPPET_AGENT_CERTNAME=#{host}'#{pe_debug}"
99
- else
100
- "cd #{host['working_dir']} && msiexec.exe /qn /L*V #{log_file} /i #{host['dist']}.msi PUPPET_MASTER_SERVER=#{master} PUPPET_AGENT_CERTNAME=#{host}#{pe_debug}"
101
- end
102
78
  # Frictionless install didn't exist pre-3.2.0, so in that case we fall
103
79
  # through and do a regular install.
104
- elsif host['roles'].include? 'frictionless' and ! version_is_less(version, '3.2.0')
80
+ if host['roles'].include? 'frictionless' and ! version_is_less(version, '3.2.0')
105
81
  # PE 3.4 introduced the ability to pass in config options to the bash script in the form
106
82
  # of <section>:<key>=<value>
107
83
  frictionless_install_opts = []
@@ -342,6 +318,7 @@ module Beaker
342
318
  # @option opts [Boolean] :fetch_local_then_push_to_host determines whether
343
319
  # you use Beaker as the middleman for this (true), or curl the
344
320
  # file from the host (false; default behavior)
321
+ # @option opts [Boolean] :masterless Are we performing a masterless installation?
345
322
  #
346
323
  # @example
347
324
  # do_install(hosts, {:type => :upgrade, :pe_dir => path, :pe_ver => version, :pe_ver_win => version_win})
@@ -352,8 +329,7 @@ module Beaker
352
329
  # @api private
353
330
  #
354
331
  def do_install hosts, opts = {}
355
- masterless = (defined? options) ? options[:masterless] : false
356
- opts[:masterless] = masterless # has to pass masterless down for answer generation awareness
332
+ masterless = opts[:masterless]
357
333
  opts[:type] = opts[:type] || :install
358
334
  unless masterless
359
335
  pre30database = version_is_less(opts[:pe_ver] || database['pe_ver'], '3.0')
@@ -414,18 +390,22 @@ module Beaker
414
390
  install_hosts.each do |host|
415
391
  if agent_only_check_needed && hosts_agent_only.include?(host)
416
392
  host['type'] = 'aio'
417
- install_puppet_agent_pe_promoted_repo_on(host, { :puppet_agent_version => opts[:puppet_agent_version],
418
- :puppet_agent_sha => opts[:puppet_agent_sha],
419
- :pe_ver => opts[:pe_ver],
420
- :puppet_collection => opts[:puppet_collection] })
421
- setup_defaults_and_config_helper_on(host, master, [0, 1])
393
+ install_puppet_agent_pe_promoted_repo_on(host, { :puppet_agent_version => host[:puppet_agent_version] || opts[:puppet_agent_version],
394
+ :puppet_agent_sha => host[:puppet_agent_sha] || opts[:puppet_agent_sha],
395
+ :pe_ver => host[:pe_ver] || opts[:pe_ver],
396
+ :puppet_collection => host[:puppet_collection] || opts[:puppet_collection] })
397
+ # 1 since no certificate found and waitforcert disabled
398
+ acceptable_exit_codes = [0, 1]
399
+ acceptable_exit_codes << 2 if opts[:type] == :upgrade
400
+ setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes)
422
401
  elsif host['platform'] =~ /windows/
423
- on host, installer_cmd(host, opts)
424
- configure_pe_defaults_on(host)
425
- if not host.is_cygwin?
426
- # HACK: for some reason, post install we need to refresh the connection to make puppet available for execution
427
- host.close
428
- end
402
+ opts = { :debug => host[:pe_debug] || opts[:pe_debug] }
403
+ msi_path = "#{host['working_dir']}\\#{host['dist']}.msi"
404
+ install_msi_on(host, msi_path, {}, opts)
405
+
406
+ # 1 since no certificate found and waitforcert disabled
407
+ acceptable_exit_codes = 1
408
+ setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes)
429
409
  else
430
410
  # We only need answers if we're using the classic installer
431
411
  version = host['pe_ver'] || opts[:pe_ver]
@@ -436,17 +416,17 @@ module Beaker
436
416
  deploy_frictionless_to_master(host)
437
417
  end
438
418
  on host, installer_cmd(host, opts)
439
- configure_pe_defaults_on(host)
419
+ configure_type_defaults_on(host)
440
420
  elsif host['platform'] =~ /osx|eos/
441
421
  # If we're not frictionless, we need to run the OSX special-case
442
422
  on host, installer_cmd(host, opts)
443
423
  acceptable_codes = host['platform'] =~ /osx/ ? [1] : [0, 1]
444
424
  setup_defaults_and_config_helper_on(host, master, acceptable_codes)
445
425
  else
446
- answers = Beaker::Answers.create(opts[:pe_ver] || host['pe_ver'], hosts, opts)
426
+ answers = BeakerAnswers::Answers.create(opts[:pe_ver] || host['pe_ver'], hosts, opts)
447
427
  create_remote_file host, "#{host['working_dir']}/answers", answers.answer_string(host)
448
428
  on host, installer_cmd(host, opts)
449
- configure_pe_defaults_on(host)
429
+ configure_type_defaults_on(host)
450
430
  end
451
431
  end
452
432
 
@@ -534,7 +514,7 @@ module Beaker
534
514
  # @return nil
535
515
  # @api private
536
516
  def setup_defaults_and_config_helper_on(host, master, acceptable_exit_codes=nil)
537
- configure_pe_defaults_on(host)
517
+ configure_type_defaults_on(host)
538
518
  #set the certname and master
539
519
  on host, puppet("config set server #{master}")
540
520
  on host, puppet("config set certname #{host}")
@@ -550,12 +530,12 @@ module Beaker
550
530
 
551
531
  #Install PE based upon host configuration and options
552
532
  #
553
- # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
554
- # or a role (String or Symbol) that identifies one or more hosts.
533
+ # @param [Host, Array<Host>] install_hosts One or more hosts to act upon
555
534
  # @!macro common_opts
535
+ # @option opts [Boolean] :masterless Are we performing a masterless installation?
556
536
  # @option opts [String] :puppet_agent_version Version of puppet-agent to install. Required for PE agent
557
537
  # only hosts on 4.0+
558
- # @option opts [String] :puppet_agent_sha The sha of puppet-agent to install, defaults to puppet-agent-version.
538
+ # @option opts [String] :puppet_agent_sha The sha of puppet-agent to install, defaults to puppet_agent_version.
559
539
  # Required for PE agent only hosts on 4.0+
560
540
  # @option opts [String] :pe_ver The version of PE (will also use host['pe_ver']), defaults to '4.0'
561
541
  # @option opts [String] :puppet_collection The puppet collection for puppet-agent install.
@@ -567,17 +547,22 @@ module Beaker
567
547
  # Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz)
568
548
  # for Unix like systems and puppet-enterprise-VERSION.msi for Windows systems.
569
549
  #
570
- def install_pe_on(hosts, opts)
571
- #process the version files if necessary
572
- confine_block(:to, {}, hosts) do
573
- hosts.each do |host|
550
+ def install_pe_on(install_hosts, opts)
551
+ confine_block(:to, {}, install_hosts) do
552
+ sorted_hosts.each do |host|
553
+ #process the version files if necessary
574
554
  host['pe_dir'] ||= opts[:pe_dir]
575
555
  if host['platform'] =~ /windows/
576
- host['pe_ver'] = host['pe_ver'] || opts['pe_ver'] ||
577
- Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || opts[:pe_dir], opts[:pe_version_file_win])
556
+ # we don't need the pe_version if:
557
+ # * master pe_ver > 4.0
558
+ if not (!opts[:masterless] && master[:pe_ver] && !version_is_less(master[:pe_ver], '3.99'))
559
+ host['pe_ver'] ||= Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || opts[:pe_dir], opts[:pe_version_file_win])
560
+ else
561
+ # inherit the master's version
562
+ host['pe_ver'] ||= master[:pe_ver]
563
+ end
578
564
  else
579
- host['pe_ver'] = host['pe_ver'] || opts['pe_ver'] ||
580
- Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || opts[:pe_dir], opts[:pe_version_file])
565
+ host['pe_ver'] ||= Beaker::Options::PEVersionScraper.load_pe_version(host[:pe_dir] || opts[:pe_dir], opts[:pe_version_file])
581
566
  end
582
567
  end
583
568
  do_install sorted_hosts, opts
@@ -591,8 +576,7 @@ module Beaker
591
576
  end
592
577
 
593
578
  #Upgrade PE based upon host configuration and options
594
- # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
595
- # or a role (String or Symbol) that identifies one or more hosts.
579
+ # @param [Host, Array<Host>] upgrade_hosts One or more hosts to act upon
596
580
  # @!macro common_opts
597
581
  # @param [String] path A path (either local directory or a URL to a listing of PE builds).
598
582
  # Will contain a LATEST file indicating the latest build to install.
@@ -603,8 +587,8 @@ module Beaker
603
587
  #
604
588
  # @note Install file names are assumed to be of the format puppet-enterprise-VERSION-PLATFORM.(tar)|(tar.gz)
605
589
  # for Unix like systems and puppet-enterprise-VERSION.msi for Windows systems.
606
- def upgrade_pe_on hosts, opts, path=nil
607
- confine_block(:to, {}, hosts) do
590
+ def upgrade_pe_on upgrade_hosts, opts, path=nil
591
+ confine_block(:to, {}, upgrade_hosts) do
608
592
  set_console_password = false
609
593
  # if we are upgrading from something lower than 3.4 then we need to set the pe console password
610
594
  if (dashboard[:pe_ver] ? version_is_less(dashboard[:pe_ver], "3.4.0") : true)
@@ -65,6 +65,46 @@ module Beaker
65
65
  end
66
66
  end
67
67
 
68
+ # Configure the provided hosts to be of their host[:type], it host[type] == nil do nothing
69
+ def configure_type_defaults_on( hosts )
70
+ block_on hosts do |host|
71
+ has_defaults = false
72
+ if host[:type]
73
+ host_type = host[:type]
74
+ # clean up the naming conventions here (some teams use foss-package, git-whatever, we need
75
+ # to correctly handle that
76
+ # don't worry about aio, that happens in the aio_version? check
77
+ host_type = case host_type
78
+ when /(\A|-)(git)|(foss)(\Z|-)/
79
+ 'foss'
80
+ when /(\A|-)pe(\Z|-)/
81
+ 'pe'
82
+ else
83
+ nil
84
+ end
85
+ if host_type
86
+ add_method = "add_#{host_type}_defaults_on"
87
+ if self.respond_to?(add_method, host)
88
+ self.send(add_method, host)
89
+ else
90
+ raise "cannot add defaults of type #{host_type} for host #{host.name} (#{add_method} not present)"
91
+ end
92
+ has_defaults = true
93
+ end
94
+ end
95
+ if aio_version?(host)
96
+ add_aio_defaults_on(host)
97
+ has_defaults = true
98
+ end
99
+ # add pathing env
100
+ if has_defaults
101
+ add_puppet_paths_on(host)
102
+ end
103
+ end
104
+ end
105
+ alias_method :configure_foss_defaults_on, :configure_type_defaults_on
106
+ alias_method :configure_pe_defaults_on, :configure_type_defaults_on
107
+
68
108
  #If the host is associated with a type remove all defaults and environment associated with that type.
69
109
  # @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
70
110
  # or a role (String or Symbol) that identifies one or more hosts.
@@ -78,6 +118,9 @@ module Beaker
78
118
  else
79
119
  raise "cannot remove defaults of type #{host['type']} associated with host #{host.name} (#{remove_method} not present)"
80
120
  end
121
+ if aio_version?(host)
122
+ remove_aio_defaults_on(host)
123
+ end
81
124
  end
82
125
  end
83
126
  end
@@ -0,0 +1,144 @@
1
+ module Beaker
2
+ module DSL
3
+ module InstallUtils
4
+ #
5
+ # This module contains methods useful for Windows installs
6
+ #
7
+ module WindowsUtils
8
+
9
+ # Given a host, returns it's system TEMP path
10
+ # @param [Host] host An object implementing {Beaker::Hosts}'s interface.
11
+ def get_temp_path(host)
12
+ # under CYGWIN %TEMP% may not be set
13
+ tmp_path = on(host, Command.new('ECHO %SYSTEMROOT%', [], { :cmdexe => true }))
14
+ tmp_path.output.gsub(/\n/, '') + '\\TEMP'
15
+ end
16
+
17
+ # Generates commands to be inserted into a Windows batch file to launch an MSI install
18
+ # @param [String] msi_path The path of the MSI - can be a local Windows style file path like
19
+ # c:\temp\puppet.msi OR a url like https://download.com/puppet.msi or file://c:\temp\puppet.msi
20
+ # @param [Hash{String=>String}] msi_opts MSI installer options
21
+ # See https://docs.puppetlabs.com/guides/install_puppet/install_windows.html#msi-properties
22
+ # @param [String] log_path The path to write the MSI log - must be a local Windows style file path
23
+ #
24
+ # @api private
25
+ def msi_install_script(msi_path, msi_opts, log_path)
26
+ # msiexec requires backslashes in file paths launched under cmd.exe start /w
27
+ url_pattern = /^(https?|file):\/\//
28
+ msi_path = msi_path.gsub(/\//, "\\") if msi_path !~ url_pattern
29
+
30
+ msi_params = msi_opts.map{|k, v| "#{k}=#{v}"}.join(' ')
31
+
32
+ # msiexec requires quotes around paths with backslashes - c:\ or file://c:\
33
+ # not strictly needed for http:// but it simplifies this code
34
+ batch_contents = <<-BATCH
35
+ start /w msiexec.exe /i \"#{msi_path}\" /qn /L*V #{log_path} #{msi_params}
36
+ exit /B %errorlevel%
37
+ BATCH
38
+ end
39
+
40
+ # Given a host, path to MSI and MSI options, will create a batch file
41
+ # on the host, returning the path to the randomized batch file and
42
+ # the randomized log file
43
+ # @param [Host] host An object implementing {Beaker::Hosts}'s interface.
44
+ # @param [String] msi_path The path of the MSI - can be a local Windows style file path like
45
+ # c:\temp\puppet.msi OR a url like https://download.com/puppet.msi or file://c:\temp\puppet.msi
46
+ # @param [Hash{String=>String}] msi_opts MSI installer options
47
+ # See https://docs.puppetlabs.com/guides/install_puppet/install_windows.html#msi-properties
48
+ # @api private
49
+ def create_install_msi_batch_on(host, msi_path, msi_opts)
50
+ timestamp = Time.new.strftime('%Y-%m-%d_%H.%M.%S')
51
+ tmp_path = get_temp_path(host)
52
+
53
+ batch_name = "install-puppet-msi-#{timestamp}.bat"
54
+ batch_path = "#{tmp_path}\\#{batch_name}"
55
+
56
+ log_path = "#{tmp_path}\\install-puppet-#{timestamp}.log"
57
+
58
+ Tempfile.open(batch_name) do |tmp_file|
59
+ batch_contents = msi_install_script(msi_path, msi_opts, log_path)
60
+
61
+ File.open(tmp_file.path, 'w') { |file| file.puts(batch_contents) }
62
+ host.do_scp_to(tmp_file.path, batch_path, {})
63
+ end
64
+
65
+ return batch_path, log_path
66
+ end
67
+
68
+ # Given hosts construct a PATH that includes puppetbindir, facterbindir and hierabindir
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
+ # @param [String] msi_path The path of the MSI - can be a local Windows style file path like
72
+ # c:\temp\puppet.msi OR a url like https://download.com/puppet.msi or file://c:\temp\puppet.msi
73
+ # @param [Hash{String=>String}] msi_opts MSI installer options
74
+ # See https://docs.puppetlabs.com/guides/install_puppet/install_windows.html#msi-properties
75
+ # @option msi_opts [String] INSTALLIDIR Where Puppet and its dependencies should be installed.
76
+ # (Defaults vary based on operating system and intaller architecture)
77
+ # Requires Puppet 2.7.12 / PE 2.5.0
78
+ # @option msi_opts [String] PUPPET_MASTER_SERVER The hostname where the puppet master server can be reached.
79
+ # (Defaults to puppet)
80
+ # Requires Puppet 2.7.12 / PE 2.5.0
81
+ # @option msi_opts [String] PUPPET_CA_SERVER The hostname where the CA puppet master server can be reached, if you are using multiple masters and only one of them is acting as the CA.
82
+ # (Defaults the value of PUPPET_MASTER_SERVER)
83
+ # Requires Puppet 2.7.12 / PE 2.5.0
84
+ # @option msi_opts [String] PUPPET_AGENT_CERTNAME The node’s certificate name, and the name it uses when requesting catalogs. This will set a value for
85
+ # (Defaults to the node's fqdn as discovered by facter fqdn)
86
+ # Requires Puppet 2.7.12 / PE 2.5.0
87
+ # @option msi_opts [String] PUPPET_AGENT_ENVIRONMENT The node’s environment.
88
+ # (Defaults to production)
89
+ # Requires Puppet 3.3.1 / PE 3.1.0
90
+ # @option msi_opts [String] PUPPET_AGENT_STARTUP_MODE Whether the puppet agent service should run (or be allowed to run)
91
+ # (Defaults to Manual - valid values are Automatic, Manual or Disabled)
92
+ # Requires Puppet 3.4.0 / PE 3.2.0
93
+ # @option msi_opts [String] PUPPET_AGENT_ACCOUNT_USER Whether the puppet agent service should run (or be allowed to run)
94
+ # (Defaults to LocalSystem)
95
+ # Requires Puppet 3.4.0 / PE 3.2.0
96
+ # @option msi_opts [String] PUPPET_AGENT_ACCOUNT_PASSWORD The password to use for puppet agent’s user account
97
+ # (No default)
98
+ # Requires Puppet 3.4.0 / PE 3.2.0
99
+ # @option msi_opts [String] PUPPET_AGENT_ACCOUNT_DOMAIN The domain of puppet agent’s user account.
100
+ # (Defaults to .)
101
+ # Requires Puppet 3.4.0 / PE 3.2.0
102
+ # @option opts [Boolean] :debug output the MSI installation log when set to true
103
+ # otherwise do not output log (false; default behavior)
104
+ #
105
+ # @example
106
+ # install_msi_on(hosts, 'c:\puppet.msi', {:debug => true})
107
+ #
108
+ # @api private
109
+ def install_msi_on(hosts, msi_path, msi_opts = {}, opts = {})
110
+ block_on hosts do | host |
111
+ msi_opts['PUPPET_AGENT_STARTUP_MODE'] ||= 'Manual'
112
+ batch_path, log_file = create_install_msi_batch_on(host, msi_path, msi_opts)
113
+
114
+ # begin / rescue here so that we can reuse existing error msg propagation
115
+ begin
116
+ # 1641 = ERROR_SUCCESS_REBOOT_INITIATED
117
+ # 3010 = ERROR_SUCCESS_REBOOT_REQUIRED
118
+ on host, Command.new("\"#{batch_path}\"", [], { :cmdexe => true }), :acceptable_exit_codes => [0, 1641, 3010]
119
+ rescue
120
+ on host, Command.new("type \"#{log_file}\"", [], { :cmdexe => true })
121
+ raise
122
+ end
123
+
124
+ if opts[:debug]
125
+ on host, Command.new("type \"#{log_file}\"", [], { :cmdexe => true })
126
+ end
127
+
128
+ if !host.is_cygwin?
129
+ # HACK: for some reason, post install we need to refresh the connection to make puppet available for execution
130
+ host.close
131
+ end
132
+
133
+ # verify service status post install
134
+ # if puppet service exists, then pe-puppet is not queried
135
+ # if puppet service does not exist, pe-puppet is queried and that exit code is used
136
+ # therefore, this command will always exit 0 if either service is installed
137
+ on host, Command.new("sc query puppet || sc query pe-puppet", [], { :cmdexe => true })
138
+ end
139
+ end
140
+
141
+ end
142
+ end
143
+ end
144
+ end
@@ -119,16 +119,25 @@ module Beaker
119
119
  host['roles'].length == 1 && host['roles'].include?('agent')
120
120
  end
121
121
 
122
- # Determine whether a host has an AIO version or not. If a host :pe_ver
122
+ # Determine whether a host has an AIO version or not. If a host :pe_ver or :version
123
123
  # is not specified, then it is open-ended, and as such, can be an AIO
124
124
  # version depending on the context.
125
125
  #
126
+ # True when any of the following cases are true
127
+ # * has PE version (:pe_ver) >= 4.0
128
+ # * has FOSS version (:version) >= 4.0
129
+ # * host has role 'aio'
130
+ # * host as the type 'aio'
131
+ #
126
132
  # @note aio version is just a base-line condition. If you want to check
127
133
  # that a host is an aio agent, refer to {#aio_agent?}.
128
134
  #
129
135
  # @return [Boolean] whether or not a host is AIO-capable
130
136
  def aio_version?(host)
131
- return !( host[:pe_ver] && version_is_less(host[:pe_ver], '4.0') )
137
+ return (( host[:pe_ver] && !version_is_less(host[:pe_ver], '4.0') ) ||
138
+ ( host[:version] && !version_is_less(host[:version], '4.0') ) ||
139
+ ( host[:roles] && host[:roles].include?('aio')) ||
140
+ ( host[:type] && !!(host[:type] =~ /(\A|-)aio(\Z|-)/ ))) == true
132
141
  end
133
142
 
134
143
  # Determine if the host is an AIO agent
@@ -140,6 +149,14 @@ module Beaker
140
149
  aio_version?(host) && agent_only(host)
141
150
  end
142
151
 
152
+ # Add the provided role to the host
153
+ #
154
+ # @param [Host] host Host to add role to
155
+ # @param [String] role The role to add to host
156
+ def add_role(host, role)
157
+ host[:roles] = host[:roles] | [role]
158
+ end
159
+
143
160
  #Create a new role method for a given arbitrary role name. Makes it possible to be able to run
144
161
  #commands without having to refer to role by String or Symbol. Will not add a new method
145
162
  #definition if the name is already in use.
@@ -156,7 +173,7 @@ module Beaker
156
173
  else
157
174
  if not respond_to? role
158
175
  if role !~ /\A[[:alpha:]]+[a-zA-Z0-9_]*[!?=]?\Z/
159
- raise "Role name format error for '#{role}'. Allowed characters are: \na-Z\n0-9 (as long as not at the beginning of name)\n'_'\n'?', '!' and '=' (only as individual last character at end of name)"
176
+ raise ArgumentError, "Role name format error for '#{role}'. Allowed characters are: \na-Z\n0-9 (as long as not at the beginning of name)\n'_'\n'?', '!' and '=' (only as individual last character at end of name)"
160
177
  end
161
178
  self.class.send :define_method, role.to_s do
162
179
  hosts_with_role = hosts_as role.to_sym
@@ -167,18 +167,29 @@ module Beaker
167
167
  # @example Confining to an already defined subset of hosts
168
168
  # confine :to, {}, agents
169
169
  #
170
+ # @example Confining from an already defined subset of hosts
171
+ # confine :except, {}, agents
172
+ #
173
+ #
170
174
  # @return [Array<Host>] Returns an array of hosts that are still valid
171
175
  # targets for this tests case.
172
176
  # @raise [SkipTest] Raises skip test if there are no valid hosts for
173
177
  # this test case after confinement.
174
178
  def confine(type, criteria, host_array = nil, &block)
175
- hosts_to_modify = host_array || hosts
179
+ hosts_to_modify = Array( host_array || hosts )
176
180
  case type
177
181
  when :except
178
- hosts_to_modify = hosts_to_modify - select_hosts(criteria, hosts_to_modify, &block)
182
+ if criteria and ( not criteria.empty? )
183
+ hosts_to_modify = hosts_to_modify - select_hosts(criteria, hosts_to_modify, &block)
184
+ else
185
+ # confining to all hosts *except* provided array of hosts
186
+ hosts_to_modify = hosts - host_array
187
+ end
179
188
  when :to
180
189
  if criteria and ( not criteria.empty? )
181
190
  hosts_to_modify = select_hosts(criteria, hosts_to_modify, &block)
191
+ else
192
+ # confining to only hosts in provided array of hosts
182
193
  end
183
194
  else
184
195
  raise "Unknown option #{type}"
@@ -198,7 +209,7 @@ module Beaker
198
209
  # @see #confine
199
210
  def confine_block(type, criteria, host_array = nil, &block)
200
211
  begin
201
- host_array ||= hosts
212
+ host_array = Array( host_array || hosts )
202
213
  original_hosts = self.hosts.dup
203
214
  confine(type, criteria, host_array)
204
215
 
@@ -0,0 +1,18 @@
1
+ module FreeBSD::Pkg
2
+ include Beaker::CommandFactory
3
+
4
+ def install_package(name, cmdline_args = nil, opts = {})
5
+ case self['platform']
6
+ when /freebsd-9/
7
+ cmdline_args ||= '-rF'
8
+ result = execute("pkg_add #{cmdline_args} #{name}", opts) { |result| result }
9
+ when /freebsd-10/
10
+ cmdline_args ||= '-y'
11
+ result = execute("pkg install #{cmdline_args} #{name}", opts) { |result| result }
12
+ else
13
+ raise "Package #{name} could not be installed on #{self}"
14
+ end
15
+ result.exit_code == 0
16
+ end
17
+
18
+ end
@@ -7,11 +7,13 @@ module FreeBSD
7
7
 
8
8
  [
9
9
  'exec',
10
+ 'pkg',
10
11
  ].each do |lib|
11
12
  require "beaker/host/freebsd/#{lib}"
12
13
  end
13
14
 
14
15
  include FreeBSD::Exec
16
+ include FreeBSD::Pkg
15
17
 
16
18
  def platform_defaults
17
19
  h = Beaker::Options::OptionsHash.new
@@ -102,11 +102,11 @@ module Unix::Exec
102
102
  env_file = self[:ssh_env_file]
103
103
  escaped_val = Regexp.escape(val).gsub('/', '\/').gsub(';', '\;')
104
104
  #see if the key/value pair already exists
105
- if exec(Beaker::Command.new("grep #{key}=.*#{escaped_val} #{env_file}"), :accept_all_exit_codes => true ).exit_code == 0
105
+ if exec(Beaker::Command.new("grep ^#{key}=.*#{escaped_val} #{env_file}"), :accept_all_exit_codes => true ).exit_code == 0
106
106
  return #nothing to do here, key value pair already exists
107
107
  #see if the key already exists
108
- elsif exec(Beaker::Command.new("grep #{key} #{env_file}"), :accept_all_exit_codes => true ).exit_code == 0
109
- exec(Beaker::SedCommand.new(self['platform'], "s/#{key}=/#{key}=#{escaped_val}:/", env_file))
108
+ elsif exec(Beaker::Command.new("grep ^#{key} #{env_file}"), :accept_all_exit_codes => true ).exit_code == 0
109
+ exec(Beaker::SedCommand.new(self['platform'], "s/^#{key}=/#{key}=#{escaped_val}:/", env_file))
110
110
  else
111
111
  exec(Beaker::Command.new("echo \"#{key}=#{val}\" >> #{env_file}"))
112
112
  end
@@ -40,6 +40,8 @@ module Unix::Pkg
40
40
  result = execute("pkg_info #{name}", opts) { |result| result }
41
41
  when /freebsd-10/
42
42
  result = execute("pkg info #{name}", opts) { |result| result }
43
+ when /openbsd/
44
+ result = execute("pkg_info #{name}", opts) { |result| result }
43
45
  else
44
46
  raise "Package #{name} cannot be queried on #{self}"
45
47
  end
@@ -63,6 +65,11 @@ module Unix::Pkg
63
65
  execute("zypper --non-interactive in #{name}", opts)
64
66
  when /el-4/
65
67
  @logger.debug("Package installation not supported on rhel4")
68
+ when /fedora-22/
69
+ if version
70
+ name = "#{name}-#{version}"
71
+ end
72
+ execute("dnf -y #{cmdline_args} install #{name}", opts)
66
73
  when /cisco|fedora|centos|eos|el-/
67
74
  if version
68
75
  name = "#{name}-#{version}"
@@ -82,6 +89,32 @@ module Unix::Pkg
82
89
  execute("pkg_add -fr #{cmdline_args} #{name}", opts)
83
90
  when /freebsd-10/
84
91
  execute("pkg #{cmdline_args} install #{name}", opts)
92
+ when /openbsd/
93
+ begin
94
+ execute("pkg_add -I #{cmdline_args} #{name}", opts) do |command|
95
+ # Handles where there are multiple rubies, installs the latest one
96
+ if command.stderr =~ /^Ambiguous: #{name} could be (.+)$/
97
+ name = $1.chomp.split(' ').collect { |x|
98
+ x =~ /-(\d[^-p]+)/
99
+ [x, $1]
100
+ }.select { |x|
101
+ # Blacklist Ruby 2.2.0+ for the sake of Puppet 3.x
102
+ Gem::Version.new(x[1]) < Gem::Version.new('2.2.0')
103
+ }.sort { |a,b|
104
+ Gem::Version.new(b[1]) <=> Gem::Version.new(a[1])
105
+ }.collect { |x|
106
+ x[0]
107
+ }.first
108
+ raise ArgumentException
109
+ end
110
+ # If the package advises symlinks to be created, do it
111
+ command.stdout.split(/\n/).select { |x| x =~ /^\s+ln\s/ }.each do |ln|
112
+ execute(ln, opts)
113
+ end
114
+ end
115
+ rescue
116
+ retry
117
+ end
85
118
  else
86
119
  raise "Package #{name} cannot be installed on #{self}"
87
120
  end
@@ -93,6 +126,8 @@ module Unix::Pkg
93
126
  execute("zypper --non-interactive rm #{name}", opts)
94
127
  when /el-4/
95
128
  @logger.debug("Package uninstallation not supported on rhel4")
129
+ when /fedora-22/
130
+ execute("dnf -y #{cmdline_args} remove #{name}", opts)
96
131
  when /cisco|fedora|centos|eos|el-/
97
132
  execute("yum -y #{cmdline_args} remove #{name}", opts)
98
133
  when /ubuntu|debian|cumulus/
@@ -117,6 +152,8 @@ module Unix::Pkg
117
152
  execute("zypper --non-interactive --no-gpg-checks up #{name}", opts)
118
153
  when /el-4/
119
154
  @logger.debug("Package upgrade is not supported on rhel4")
155
+ when /fedora-22/
156
+ execute("dnf -y #{cmdline_args} update #{name}", opts)
120
157
  when /cisco|fedora|centos|eos|el-/
121
158
  execute("yum -y #{cmdline_args} update #{name}", opts)
122
159
  when /ubuntu|debian|cumulus/
@@ -3,6 +3,9 @@ module Windows::Exec
3
3
 
4
4
  def reboot
5
5
  exec(Beaker::Command.new('shutdown /r /t 0 /d p:4:1 /c "Beaker::Host reboot command issued"'), :expect_connection_failure => true)
6
+ # rebooting on windows is sloooooow
7
+ # give it some breathing room before attempting a reconnect
8
+ sleep(30)
6
9
  end
7
10
 
8
11
  ABS_CMD = 'c:\\\\windows\\\\system32\\\\cmd.exe'