Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(MODULES-8431) Update windows installation to use powershell #362

Merged
merged 1 commit into from Jan 16, 2019

Conversation

mcdonaldseanp
Copy link
Contributor

This commit updates the installation method on windows to use a powershell
script rather than a batch file

@mcdonaldseanp mcdonaldseanp force-pushed the powershell branch 2 times, most recently from 778a293 to 75c9fbb Compare January 11, 2019 23:03
@mcdonaldseanp mcdonaldseanp requested review from caseywilliams, ekinanp and branan and removed request for caseywilliams January 11, 2019 23:04
@branan
Copy link
Contributor

branan commented Jan 11, 2019

I think you forgot to git rm the batch file

@mcdonaldseanp
Copy link
Contributor Author

whoops

@puppetcla
Copy link

CLA signed by all contributors.

[String] $InstallDir='<%= @install_dir %>',
[String] $PuppetMaster='<%= @_puppet_master %>',
[String] $PuppetStartType='<%= @_agent_startup_mode %>',
[String] $InstallArgs='<%= @_install_options.map do |option|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I'd prefer this "logic" be in the manifest and the $InstallArgs be a dumb string, but I'm not too fussed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Glenn

Fetch the location of the puppet installation from the registry
#>
function Script:fetch_install_dir {
param()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without cmdlet binding, this is not needed.

Move/rename puppetres.dll to a temporary location
#>
function Script:move_puppetres_dll {
param()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without cmdlet binding, this is not needed.

# for the already installed package
$InstallDir = fetch_install_dir
if (Test-Path "$InstallDir\puppet\bin\puppetres.dll") {
Out-File -InputObject "Moving puppetres.dll to $temp_puppetres" -FilePath $Logfile -Append
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The canonical way of doing this is normally;

"Moving puppetres.dll to $temp_puppetres" | Out-File -FilePath $Logfile -Append"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be easier to abstract this into a method? Log <error_message> is more readable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to refactor to it's own function

$InstallDir = fetch_install_dir
if (Test-Path $temp_puppetres) {
Out-File -InputObject "Restoring puppetres.dll" -FilePath $Logfile -Append
Move-Item -Path $temp_puppetres -Destination "$InstallDir\puppet\bin\puppetres.dll"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should fail gracefully. Moving the locked DLL will fail on rollback

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can also test for the existence of the normal puppetres.dll too.

# but using WaitForExit works fine.
$msiexec_proc.WaitForExit()
Out-File -InputObject "****************************** End msiexec.exe output ******************************" -FilePath $Logfile -Append
if ($msiexec_proc.ExitCode -ne 0){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite true.

Exit code could be 0, 3010 or 1641 (? I think it's 1604 - Soft reboot required)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we force /norestart in the MSI wouldn't the restart exit codes technically be failures as far as we're concerned?

It's my understanding that the agent should absolutely never cause a restart on upgrade.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are both successful installation methods. 1641 simply means "The requested operation completed successfully. The system will be restarted so the changes can take effect" and 3010 means "The requested operation is successful. Changes will not be effective until the system is rebooted."

Thus one is initiating the reboot automatically, and the other is waiting for the user to reboot. The difference is in the switches or properties you've set when calling the MSI.

3010 is a succesfull install but it's pending a reboot. i.e. the agent package WANTED to reboot but it couldn't due to /norestart

Copy link
Contributor

@glennsarti glennsarti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per comments.

  • Also strictly speaking the function names in the PS script aren't Verb-Noun
  • Are we looking at running Pester tests over this?
  • This script will need manual verification (i.e. functional review) for it's ability to upgrade, and will need to prove the puppetres.dll workaround works.

@ekinanp
Copy link
Contributor

ekinanp commented Jan 12, 2019

@glennsarti I was looking into https://tickets.puppetlabs.com/browse/PA-663 to understand the puppetres.dll issue and it looks like your comment on 2017/03/01 seems to imply that the workaround is only necessary until https://tickets.puppetlabs.com/browse/PA-768 is resolved?

# Wait for the puppet run to finish
Out-File -InputObject "Waiting for puppet to stop, PID:$PuppetPID" -FilePath $Logfile -Append
try {
Wait-Process -Id $PuppetPID -Timeout 120
Copy link
Contributor

@ekinanp ekinanp Jan 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also do

$proc = Get-Process -Id $PuppetPID -ErrorAction SilentlyContinue
if ($proc) {
  if (-Not $proc.WaitForExit(120000)) { // milliseconds
    Log "Timeout"
  }
} else {
  Log "Puppet was already finished"
}

when waiting for the Puppet run to end.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or more simply;

try {
  Get-Process -ID $PuppetPID -ErrorAction SilentlyContinue | Wait-Process -Timeout 120`
}
catch [System.TimeoutException] {
 # TIMEOUT OCCURED
}
catch {
  # unhandled error
}

throw $_
}
}
foreach($service in $service_names) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous install_puppet.bat file had a long comment about why stopping the services was important. Maybe that comment should be here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The long comment was about waiting for the puppet PID, rather than waiting for the services:

https://github.com/puppetlabs/puppetlabs-puppet_agent/blob/master/templates/install_puppet.bat.erb#L35L40

but I will add that back. And I'll add a comment as to why we need to shut down the services

@mcdonaldseanp mcdonaldseanp force-pushed the powershell branch 9 times, most recently from 9e3164f to 6aba3a6 Compare January 15, 2019 00:53
@glennsarti
Copy link
Contributor

Pending manual Functional Review of the DLL locking repro case and description of manual testing performed by @mcdonaldseanp

@mcdonaldseanp mcdonaldseanp force-pushed the powershell branch 4 times, most recently from 7acc4ba to 122896c Compare January 15, 2019 21:56
Lock-Installation $install_pid_lock
# Wait for the puppet run to finish
#
# We *must* wait for Puppet Agent to finished applying its prior catalog
Copy link
Contributor

@ekinanp ekinanp Jan 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Typo here -- "to finished applying".

Also, this comment should be expanded on a bit more. Probably something like

We must wait for the puppet agent to finish applying its catalog before we stop all of our services. Otherwise if the catalog has additional resources that start our services (e.g. such as those from the PE module), then the install will fail to proceed.

i.e. it isn't something specific to pxp-agent, it applies to all services.

[String] $message
)
begin {
$message | Out-File -FilePath $Logfile -Append
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth including a timestamp.

This commit updates the installation method on windows to use a powershell
script rather than a batch file
$InstallDir = Find-InstallDir
if (Test-Path "$InstallDir\puppet\bin\puppetres.dll") {
Write-Log "Moving puppetres.dll to $temp_puppetres"
Move-Item -Path "$InstallDir\puppet\bin\puppetres.dll" -Destination $temp_puppetres
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 I wasn't sure if this would work the same in PowerShell. Guess it does :-)

Copy link
Contributor

@glennsarti glennsarti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ship it!

@glennsarti
Copy link
Contributor

Pic

@ekinanp ekinanp merged commit 4867935 into puppetlabs:master Jan 16, 2019
# correctly from the module.
[parameter(Position=0)]
[String] $PuppetPID,
[String] $Source='<%= @_msi_location %>',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should be passing params via the invocation, rather than injecting values into a template. I think the only reason we used a template on the batch side was because arg passing with batch can be a real pain.

# PuppetPID _must_ come first!, this script needs PuppetPID to be a positional parameter to execute
# correctly from the module.
[parameter(Position=0)]
[String] $PuppetPID,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a numeric value, shouldn't it?

)
# Find-InstallDir, Move-PuppetresDLL and Reset-PuppetresDLL serve as a workaround for older
# installations of puppet: we used to need to move puppetres.dll out of the way during puppet
# upgrades because the file would lock and cause network stack restarts.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's one possibility, but the problem was a little more subtle.

Any application connected to the event log - like a local event log collector application or the PowerShell cmdlets, loads / locks all the resource DLLs registered to the event log. This means the event log service holds a lock on puppetres.dll.

Because Windows will share svchost.exe instances amongst multiple services, any services co-located to the same process as the event log will get bounced, even when just the event log service needs to be restarted. Furthermore, in the event that one of the other hosted services was a dependency of yet other services, it would cause a domino effect. So in the worst case situation, I believe we saw DHCP being shared with Event Log. Higher level services like Exchange Server depended on DHCP, which caused cascading service restarts.

It may be sufficient to mention network stack restarts, but that's just one of the ways this manifested depending on version of OS, which services Windows decided to share with event log, etc, etc.

Fetch the location of the puppet installation from the registry
#>
function Script:Find-InstallDir {
begin {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this function doesn't accept pipeline input, begin, process, end are superfluous.

@@ -0,0 +1,262 @@
# install_puppet.ps1
<#
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a similar note about this being a template... these inline docs won't inform the PowerShell runtime during test / debugging since it's not a PS1 until rendered.

Reset-PuppetresDLL $temp_puppetres
}
} finally {
Unlock-Installation $install_pid_lock
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you also want to call Unregister-Event to perform cleanup... though exiting PowerShell altogether may do this for you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants