From 70083fc8697147abcfa312acacdf42eb0c7d7f25 Mon Sep 17 00:00:00 2001 From: Gonzalo Peci Date: Fri, 7 Aug 2015 12:18:17 +1200 Subject: [PATCH 1/4] Add reboot checks before moving out of the reboot. --- provisioner/windows-restart/provisioner.go | 32 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 2e4b7c371..26736344e 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -16,6 +16,8 @@ import ( var DefaultRestartCommand = "powershell \"& {Restart-Computer -force }\"" var DefaultRestartCheckCommand = winrm.Powershell(`echo "${env:COMPUTERNAME} restarted."`) var retryableSleep = 5 * time.Second +var TryCheckReboot = "shutdown.exe -f -r -t 60" +var AbortReboot = "shutdown.exe -a" type Config struct { common.PackerConfig `mapstructure:",squash"` @@ -94,16 +96,42 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error { return fmt.Errorf("Restart script exited with non-zero exit status: %d", cmd.ExitStatus) } - return waitForRestart(p) + return waitForRestart(p, comm) } -var waitForRestart = func(p *Provisioner) error { +var waitForRestart = func(p *Provisioner, comm packer.Communicator) error { ui := p.ui ui.Say("Waiting for machine to restart...") waitDone := make(chan bool, 1) timeout := time.After(p.config.RestartTimeout) var err error + p.comm = comm + var cmd *packer.RemoteCmd + trycommand := TryCheckReboot + abortcommand := AbortReboot + // Stolen from Vagrant reboot checker + for { + log.Printf("Check if machine is rebooting...") + cmd = &packer.RemoteCmd{Command: trycommand} + err = cmd.StartWithUi(comm, ui) + if err != nil { + // Couldnt execute, we asume machine is rebooting already + break + } + if cmd.ExitStatus != 0 { + // Reboot already in progress but not completed + log.Printf("Reboot already in progress, waiting...") + time.Sleep(10 * time.Second) + } + if cmd.ExitStatus == 0 { + // Cancel reboot we created to test if machine was already rebooting + cmd = &packer.RemoteCmd{Command: abortcommand} + cmd.StartWithUi(comm, ui) + break + } + } + go func() { log.Printf("Waiting for machine to become available...") err = waitForCommunicator(p) From 08359e409a0df8c3060dd56cfb1d0e4b5740f659 Mon Sep 17 00:00:00 2001 From: Gonzalo Peci Date: Wed, 19 Aug 2015 16:09:43 +1200 Subject: [PATCH 2/4] Revert the shutdown command with new parameters as this will return proper exit codes. This will work in the same way as restart-computer -force when using ```shutdown /r /f /t 0```. Note:The WinRM library does not return the exit code currently, this will be implemented on https://github.com/masterzen/winrm/pull/26 --- provisioner/windows-restart/provisioner.go | 2 +- provisioner/windows-restart/provisioner_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 26736344e..c5a683b73 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -13,7 +13,7 @@ import ( "github.com/mitchellh/packer/template/interpolate" ) -var DefaultRestartCommand = "powershell \"& {Restart-Computer -force }\"" +var DefaultRestartCommand = "shutdown /r /f /t 0 /c \"packer restart\"" var DefaultRestartCheckCommand = winrm.Powershell(`echo "${env:COMPUTERNAME} restarted."`) var retryableSleep = 5 * time.Second var TryCheckReboot = "shutdown.exe -f -r -t 60" diff --git a/provisioner/windows-restart/provisioner_test.go b/provisioner/windows-restart/provisioner_test.go index 247452c22..7a38e1579 100644 --- a/provisioner/windows-restart/provisioner_test.go +++ b/provisioner/windows-restart/provisioner_test.go @@ -35,7 +35,7 @@ func TestProvisionerPrepare_Defaults(t *testing.T) { t.Errorf("unexpected remote path: %s", p.config.RestartTimeout) } - if p.config.RestartCommand != "powershell \"& {Restart-Computer -force }\"" { + if p.config.RestartCommand != "shutdown /r /f /t 0 /c \"packer restart\"" { t.Errorf("unexpected remote path: %s", p.config.RestartCommand) } } From 8bd3e62853b7b8f4875c7bdbbffab193a0877dfa Mon Sep 17 00:00:00 2001 From: Gonzalo Peci Date: Wed, 19 Aug 2015 16:05:22 +1200 Subject: [PATCH 3/4] Because the new functionality makes the ```waitForRestart()``` function run commands it modifies the value of ```comm.StartCmd.Command``` that is being checked, we need to implement the same workaround that is being used for the ```waitForCommunicator()``` function. This should make the test work again and retain functionality. --- provisioner/windows-restart/provisioner_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/provisioner/windows-restart/provisioner_test.go b/provisioner/windows-restart/provisioner_test.go index 7a38e1579..f91f5a771 100644 --- a/provisioner/windows-restart/provisioner_test.go +++ b/provisioner/windows-restart/provisioner_test.go @@ -100,6 +100,10 @@ func TestProvisionerProvision_Success(t *testing.T) { waitForCommunicator = func(p *Provisioner) error { return nil } + waitForRestartOld := waitForRestart + waitForRestart = func(p *Provisioner, comm packer.Communicator) error { + return nil + } err := p.Provision(ui, comm) if err != nil { t.Fatal("should not have error") @@ -113,6 +117,7 @@ func TestProvisionerProvision_Success(t *testing.T) { } // Set this back! waitForCommunicator = waitForCommunicatorOld + waitForRestart = waitForRestartOld } func TestProvisionerProvision_CustomCommand(t *testing.T) { @@ -131,6 +136,10 @@ func TestProvisionerProvision_CustomCommand(t *testing.T) { waitForCommunicator = func(p *Provisioner) error { return nil } + waitForRestartOld := waitForRestart + waitForRestart = func(p *Provisioner, comm packer.Communicator) error { + return nil + } err := p.Provision(ui, comm) if err != nil { t.Fatal("should not have error") @@ -142,6 +151,7 @@ func TestProvisionerProvision_CustomCommand(t *testing.T) { } // Set this back! waitForCommunicator = waitForCommunicatorOld + waitForRestart = waitForRestartOld } func TestProvisionerProvision_RestartCommandFail(t *testing.T) { From f01f62dc88e81c2133e898eaa86c8881e4b3c75b Mon Sep 17 00:00:00 2001 From: Gonzalo Peci Date: Thu, 20 Aug 2015 09:00:38 +1200 Subject: [PATCH 4/4] Add specific exit codes listed on https://msdn.microsoft.com/en-us/library/windows/desktop/ms681383(v=vs.85).aspx --- provisioner/windows-restart/provisioner.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index c5a683b73..6b7bd90d3 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -119,7 +119,7 @@ var waitForRestart = func(p *Provisioner, comm packer.Communicator) error { // Couldnt execute, we asume machine is rebooting already break } - if cmd.ExitStatus != 0 { + if cmd.ExitStatus == 1115 || cmd.ExitStatus == 1190 { // Reboot already in progress but not completed log.Printf("Reboot already in progress, waiting...") time.Sleep(10 * time.Second)