Module: Beaker::DSL::InstallUtils::WindowsUtils

Included in:
FOSSUtils, BeakerPuppet
Defined in:
lib/beaker-puppet/install_utils/windows_utils.rb

Overview

This module contains methods useful for Windows installs

Instance Method Summary collapse

Instance Method Details

#create_install_msi_batch_on(host, msi_path, msi_opts) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Given a host, path to MSI and MSI options, will create a batch file

on the host, returning the path to the randomized batch file and
the randomized log file

Parameters:

Returns:

  • (String, String)

    path to the batch file, patch to the log file



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/beaker-puppet/install_utils/windows_utils.rb', line 54

def create_install_msi_batch_on(host, msi_path, msi_opts)
  timestamp = Time.new.strftime('%Y-%m-%d_%H.%M.%S')
  tmp_path = host.system_temp_path
  tmp_path.gsub!('/', '\\')

  batch_name = "install-puppet-msi-#{timestamp}.bat"
  batch_path = "#{tmp_path}#{host.scp_separator}#{batch_name}"
  log_path = "#{tmp_path}\\install-puppet-#{timestamp}.log"

  Tempfile.open(batch_name) do |tmp_file|
    batch_contents = msi_install_script(msi_path, msi_opts, log_path)

    File.open(tmp_file.path, 'w') { |file| file.puts(batch_contents) }
    host.do_scp_to(tmp_file.path, batch_path, {})
  end

  [batch_path, log_path]
end

#generic_install_msi_on(hosts, msi_path, msi_opts = {}, opts = {}) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Installs a specified msi path on given hosts

Examples:

generic_install_msi_on(hosts, 'https://releases.hashicorp.com/vagrant/1.8.4/vagrant_1.8.4.msi', {}, {:debug => true})

Parameters:

  • hosts (Host, Array<Host>, String, Symbol)

    One or more hosts to act upon, or a role (String or Symbol) that identifies one or more hosts.

  • msi_path (String)

    The path of the MSI - can be a local Windows style file path like c:tempfoo.msi OR a url like download.com/foo.msi or file://c:tempfoo.msi

  • msi_opts (Hash{String=>String}) (defaults to: {})

    MSI installer options

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :debug (Boolean)

    output the MSI installation log when set to true otherwise do not output log (false; default behavior)



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/beaker-puppet/install_utils/windows_utils.rb', line 205

def generic_install_msi_on(hosts, msi_path, msi_opts = {}, opts = {})
  block_on hosts do |host|
    batch_path, log_file = create_install_msi_batch_on(host, msi_path, msi_opts)
    # Powershell command looses an escaped slash resulting in cygwin relative path
    # See https://github.com/puppetlabs/beaker/pull/1626#issuecomment-621341555
    log_file_escaped = log_file.gsub('\\', '\\\\\\')
    # begin / rescue here so that we can reuse existing error msg propagation
    begin
      # 1641 = ERROR_SUCCESS_REBOOT_INITIATED
      # 3010 = ERROR_SUCCESS_REBOOT_REQUIRED
      on host, Command.new("\"#{batch_path}\"", [], { cmdexe: true }), acceptable_exit_codes: [0, 1641, 3010]
    rescue StandardError
      logger.info(file_contents_on(host, log_file_escaped))

      raise
    end

    logger.info(file_contents_on(host, log_file_escaped)) if opts[:debug]

    host.close unless host.is_cygwin?
  end
end

#get_system_temp_path(host) ⇒ String Also known as: get_temp_path

Given a host, returns it’s system TEMP path

Parameters:

  • host (Host)

    An object implementing Hosts‘s interface.

Returns:

  • (String)

    system temp path



13
14
15
# File 'lib/beaker-puppet/install_utils/windows_utils.rb', line 13

def get_system_temp_path(host)
  host.system_temp_path
end

#install_msi_on(hosts, msi_path, msi_opts = {}, opts = {}) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Given hosts construct a PATH that includes puppetbindir, facterbindir and hierabindir

Examples:

install_msi_on(hosts, 'c:\puppet.msi', {:debug => true})

Parameters:

  • hosts (Host, Array<Host>, String, Symbol)

    One or more hosts to act upon, or a role (String or Symbol) that identifies one or more hosts.

  • msi_path (String)

    The path of the MSI - can be a local Windows style file path like c:temppuppet.msi OR a url like download.com/puppet.msi or file://c:temppuppet.msi

  • msi_opts (Hash{String=>String}) (defaults to: {})
  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (msi_opts):

  • INSTALLIDIR (String)

    Where Puppet and its dependencies should be installed. (Defaults vary based on operating system and intaller architecture) Requires Puppet 2.7.12 / PE 2.5.0

  • PUPPET_MASTER_SERVER (String)

    The hostname where the puppet master server can be reached. (Defaults to puppet) Requires Puppet 2.7.12 / PE 2.5.0

  • PUPPET_CA_SERVER (String)

    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. (Defaults the value of PUPPET_MASTER_SERVER) Requires Puppet 2.7.12 / PE 2.5.0

  • PUPPET_AGENT_CERTNAME (String)

    The node’s certificate name, and the name it uses when requesting catalogs. This will set a value for (Defaults to the node’s fqdn as discovered by facter fqdn) Requires Puppet 2.7.12 / PE 2.5.0

  • PUPPET_AGENT_ENVIRONMENT (String)

    The node’s environment. (Defaults to production) Requires Puppet 3.3.1 / PE 3.1.0

  • PUPPET_AGENT_STARTUP_MODE (String)

    Whether the puppet agent service should run (or be allowed to run) (Defaults to Manual - valid values are Automatic, Manual or Disabled) Requires Puppet 3.4.0 / PE 3.2.0

  • PUPPET_AGENT_ACCOUNT_USER (String)

    Whether the puppet agent service should run (or be allowed to run) (Defaults to LocalSystem) Requires Puppet 3.4.0 / PE 3.2.0

  • PUPPET_AGENT_ACCOUNT_PASSWORD (String)

    The password to use for puppet agent’s user account (No default) Requires Puppet 3.4.0 / PE 3.2.0

  • PUPPET_AGENT_ACCOUNT_DOMAIN (String)

    The domain of puppet agent’s user account. (Defaults to .) Requires Puppet 3.4.0 / PE 3.2.0

Options Hash (opts):

  • :debug (Boolean)

    output the MSI installation log when set to true otherwise do not output log (false; default behavior)



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/beaker-puppet/install_utils/windows_utils.rb', line 114

def install_msi_on(hosts, msi_path, msi_opts = {}, opts = {})
  block_on hosts do |host|
    msi_opts['PUPPET_AGENT_STARTUP_MODE'] ||= 'Manual'
    batch_path, log_file = create_install_msi_batch_on(host, msi_path, msi_opts)
    # Powershell command looses an escaped slash resulting in cygwin relative path
    # See https://github.com/puppetlabs/beaker/pull/1626#issuecomment-621341555
    log_file_escaped = log_file.gsub('\\', '\\\\\\')
    # begin / rescue here so that we can reuse existing error msg propagation
    begin
      # 1641 = ERROR_SUCCESS_REBOOT_INITIATED
      # 3010 = ERROR_SUCCESS_REBOOT_REQUIRED
      on host, Command.new("\"#{batch_path}\"", [], { cmdexe: true }), acceptable_exit_codes: [0, 1641, 3010]
    rescue StandardError
      logger.info(file_contents_on(host, log_file_escaped))
      raise
    end

    logger.info(file_contents_on(host, log_file_escaped)) if opts[:debug]

    unless host.is_cygwin?
      # Enable the PATH updates
      host.close

      # Some systems require a full reboot to trigger the enabled path
      host.reboot unless on(host, Command.new('puppet -h', [], { cmdexe: true }),
                            accept_all_exit_codes: true).exit_code == 0
    end

    # verify service status post install
    # if puppet service exists, then pe-puppet is not queried
    # if puppet service does not exist, pe-puppet is queried and that exit code is used
    # therefore, this command will always exit 0 if either service is installed
    #
    # We also take advantage of this output to verify the startup
    # settings are honored as supplied to the MSI
    on host, Command.new('sc qc puppet || sc qc pe-puppet', [], { cmdexe: true }) do |result|
      output = result.stdout
      startup_mode = msi_opts['PUPPET_AGENT_STARTUP_MODE'].upcase

      search = case startup_mode
               when 'AUTOMATIC'
                 { code: 2, name: 'AUTO_START' }
               when 'MANUAL'
                 { code: 3, name: 'DEMAND_START' }
               when 'DISABLED'
                 { code: 4, name: 'DISABLED' }
               end

      if output !~ /^\s+START_TYPE\s+:\s+#{search[:code]}\s+#{search[:name]}/
        raise "puppet service startup mode did not match supplied MSI option '#{startup_mode}'"
      end
    end

    # (PA-514) value for PUPPET_AGENT_STARTUP_MODE should be present in
    # registry and honored after install/upgrade.
    reg_key = if host.is_x86_64?
                'HKLM\\SOFTWARE\\Wow6432Node\\Puppet Labs\\PuppetInstaller'
              else
                'HKLM\\SOFTWARE\\Puppet Labs\\PuppetInstaller'
              end
    reg_query_command = %(reg query "#{reg_key}" /v "RememberedPuppetAgentStartupMode" | findstr #{msi_opts['PUPPET_AGENT_STARTUP_MODE']})
    on host, Command.new(reg_query_command, [], { cmdexe: true })

    # emit the misc/versions.txt file which contains component versions for
    # puppet, facter, hiera, pxp-agent, packaging and vendored Ruby
    [
      "'%PROGRAMFILES%\\Puppet Labs\\puppet\\misc\\versions.txt'",
      "'%PROGRAMFILES(X86)%\\Puppet Labs\\puppet\\misc\\versions.txt'",
    ].each do |path|
      result = on(host, "cmd /c type #{path}", accept_all_exit_codes: true)
      if result.exit_code == 0
        logger.info(result.stdout)
        break
      end
    end
  end
end

#msi_install_script(msi_path, msi_opts, log_path) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Generates commands to be inserted into a Windows batch file to launch an MSI install

Parameters:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/beaker-puppet/install_utils/windows_utils.rb', line 26

def msi_install_script(msi_path, msi_opts, log_path)
  # msiexec requires backslashes in file paths launched under cmd.exe start /w
  url_pattern = %r{^(https?|file)://}
  msi_path = msi_path.gsub(%r{/}, '\\') if msi_path !~ url_pattern

  msi_params = msi_opts.map { |k, v| "#{k}=#{v}" }.join(' ')

  # msiexec requires quotes around paths with backslashes - c:\ or file://c:\
  # not strictly needed for http:// but it simplifies this code
  batch_contents = <<~BATCH
    start /w msiexec.exe /i "#{msi_path}" /qn /L*V #{log_path} #{msi_params}
    exit /B %errorlevel%
  BATCH
end