From d81871171c2b5f0bac5bb2b07c270f36adf34709 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 7 Nov 2017 15:01:02 -0800 Subject: [PATCH 1/9] make restart command work correctly even if user has their own check command --- provisioner/windows-restart/provisioner.go | 38 ++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 9b77c097e..e3cb1d119 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -17,7 +17,7 @@ import ( ) var DefaultRestartCommand = "shutdown /r /f /t 0 /c \"packer restart\"" -var DefaultRestartCheckCommand = winrm.Powershell(`if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'}; echo "${env:COMPUTERNAME} restarted."`) +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" @@ -174,28 +174,40 @@ WaitLoop: } var waitForCommunicator = func(p *Provisioner) error { + runCustomRestartCheck := true for { - cmd := &packer.RemoteCmd{Command: p.config.RestartCheckCommand} - var buf, buf2 bytes.Buffer - cmd.Stdout = &buf - cmd.Stdout = io.MultiWriter(cmd.Stdout, &buf2) select { case <-p.cancel: log.Println("Communicator wait canceled, exiting loop") return fmt.Errorf("Communicator wait canceled") case <-time.After(retryableSleep): } + if runCustomRestartCheck == true { + if p.config.RestartCheckCommand == DefaultRestartCheckCommand { + runCustomRestartCheck = false + } + // this is the user configurable command + cmdRestartCheck := &packer.RemoteCmd{Command: p.config.RestartCheckCommand} + log.Printf("Checking that communicator is connected with: '%s'", + cmdRestartCheck.Command) + // run user-configured restart check + err := cmdRestartCheck.StartWithUi(p.comm, p.ui) - log.Printf("Checking that communicator is connected with: '%s'", cmd.Command) - - err := cmd.StartWithUi(p.comm, p.ui) - - if err != nil { - log.Printf("Communication connection err: %s", err) - continue + if err != nil { + log.Printf("Communication connection err: %s", err) + continue + } + log.Printf("Connected to machine") + runCustomRestartCheck = false } + // this is the non-user-configurable check that powershell + // modules have loaded + cmdModuleLoad := &packer.RemoteCmd{Command: DefaultRestartCheckCommand} + var buf, buf2 bytes.Buffer + cmdModuleLoad.Stdout = &buf + cmdModuleLoad.Stdout = io.MultiWriter(cmdModuleLoad.Stdout, &buf2) - log.Printf("Connected to machine") + cmdModuleLoad.StartWithUi(p.comm, p.ui) stdoutToRead := buf2.String() if !strings.Contains(stdoutToRead, "restarted.") { log.Printf("echo didn't succeed; retrying...") From b52ba4557ecf67450ed75f3480dd3645d714ecda Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 7 Nov 2017 16:00:40 -0800 Subject: [PATCH 2/9] add some example json to windows restart_check_command --- website/source/docs/provisioners/windows-restart.html.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/website/source/docs/provisioners/windows-restart.html.md b/website/source/docs/provisioners/windows-restart.html.md index 450977b55..e9e348755 100644 --- a/website/source/docs/provisioners/windows-restart.html.md +++ b/website/source/docs/provisioners/windows-restart.html.md @@ -42,7 +42,14 @@ Optional parameters: detect it is rebooting. - `restart_check_command` (string) - A command to execute to check if the - restart succeeded. This will be done in a loop. + restart succeeded. This will be done in a loop. Example usage: + +``` json + { + "type": "windows-restart", + "restart_check_command": "powershell -command \"& {Write-Output 'restarted.'}\"" + }, +``` - `restart_timeout` (string) - The timeout to wait for the restart. By default this is 5 minutes. Example value: `5m`. If you are installing From d71bc34dfc1be82c9bc89dc75329c3fb7def369e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 11:49:12 -0800 Subject: [PATCH 3/9] don't need this in a loop --- provisioner/windows-restart/provisioner.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index e3cb1d119..48a3cb50a 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -175,6 +175,13 @@ WaitLoop: var waitForCommunicator = func(p *Provisioner) error { runCustomRestartCheck := true + if p.config.RestartCheckCommand == DefaultRestartCheckCommand { + runCustomRestartCheck = false + } + // this is the user configurable command + cmdRestartCheck := &packer.RemoteCmd{Command: p.config.RestartCheckCommand} + log.Printf("Checking that communicator is connected with: '%s'", + cmdRestartCheck.Command) for { select { case <-p.cancel: @@ -183,16 +190,8 @@ var waitForCommunicator = func(p *Provisioner) error { case <-time.After(retryableSleep): } if runCustomRestartCheck == true { - if p.config.RestartCheckCommand == DefaultRestartCheckCommand { - runCustomRestartCheck = false - } - // this is the user configurable command - cmdRestartCheck := &packer.RemoteCmd{Command: p.config.RestartCheckCommand} - log.Printf("Checking that communicator is connected with: '%s'", - cmdRestartCheck.Command) // run user-configured restart check err := cmdRestartCheck.StartWithUi(p.comm, p.ui) - if err != nil { log.Printf("Communication connection err: %s", err) continue From a739623d9b3232acd1a6e46642a3caefe8652d6b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 14:44:26 -0800 Subject: [PATCH 4/9] don't pipe restarted stuff through the ui --- provisioner/windows-restart/provisioner.go | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 48a3cb50a..cd261da56 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -3,7 +3,7 @@ package restart import ( "bytes" "fmt" - "io" + "log" "strings" "sync" @@ -201,13 +201,18 @@ var waitForCommunicator = func(p *Provisioner) error { } // this is the non-user-configurable check that powershell // modules have loaded - cmdModuleLoad := &packer.RemoteCmd{Command: DefaultRestartCheckCommand} - var buf, buf2 bytes.Buffer - cmdModuleLoad.Stdout = &buf - cmdModuleLoad.Stdout = io.MultiWriter(cmdModuleLoad.Stdout, &buf2) + var buf bytes.Buffer + cmdModuleLoad := &packer.RemoteCmd{ + Command: DefaultRestartCheckCommand, + Stdin: nil, + Stdout: &buf, + Stderr: &buf} - cmdModuleLoad.StartWithUi(p.comm, p.ui) - stdoutToRead := buf2.String() + // cmdModuleLoad.StartWithUi(p.comm, p.ui) + p.comm.Start(cmdModuleLoad) + cmdModuleLoad.Wait() + + stdoutToRead := buf.String() if !strings.Contains(stdoutToRead, "restarted.") { log.Printf("echo didn't succeed; retrying...") continue From e56a6dc9a0d0e050d82bd6651175aa9670904e4f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 14:55:12 -0800 Subject: [PATCH 5/9] add some comments --- provisioner/windows-restart/provisioner.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index cd261da56..c87c3a4bb 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -178,7 +178,10 @@ var waitForCommunicator = func(p *Provisioner) error { if p.config.RestartCheckCommand == DefaultRestartCheckCommand { runCustomRestartCheck = false } - // this is the user configurable command + // This command is configurable by the user to make sure that the + // vm has met their necessary criteria for having restarted. If the + // user doesn't set a special restart command, we just run the + // default as cmdModuleLoad below. cmdRestartCheck := &packer.RemoteCmd{Command: p.config.RestartCheckCommand} log.Printf("Checking that communicator is connected with: '%s'", cmdRestartCheck.Command) @@ -199,8 +202,16 @@ var waitForCommunicator = func(p *Provisioner) error { log.Printf("Connected to machine") runCustomRestartCheck = false } - // this is the non-user-configurable check that powershell - // modules have loaded + + // This is the non-user-configurable check that powershell + // modules have loaded. + + // If we catch the restart in just the right place, we will be able + // to run the restart check but the output will be an error message + // about how it needs powershell modules to load, and we will start + // provisioning before powershell is actually ready. + // In this next check, we parse stdout to make sure that the command is + // actually running as expected. var buf bytes.Buffer cmdModuleLoad := &packer.RemoteCmd{ Command: DefaultRestartCheckCommand, From 73b6247fd2450384b55afe9910e7c5796a555f03 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 15:04:25 -0800 Subject: [PATCH 6/9] remove unnecessary boolean operator --- 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 c87c3a4bb..cfa4df5e0 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -192,7 +192,7 @@ var waitForCommunicator = func(p *Provisioner) error { return fmt.Errorf("Communicator wait canceled") case <-time.After(retryableSleep): } - if runCustomRestartCheck == true { + if runCustomRestartCheck { // run user-configured restart check err := cmdRestartCheck.StartWithUi(p.comm, p.ui) if err != nil { From 6019e415441e2e4d8d812c15e3e9137df71bb053 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 15:18:43 -0800 Subject: [PATCH 7/9] dont read stderr --- provisioner/windows-restart/provisioner.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index cfa4df5e0..4107fea1b 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -216,10 +216,8 @@ var waitForCommunicator = func(p *Provisioner) error { cmdModuleLoad := &packer.RemoteCmd{ Command: DefaultRestartCheckCommand, Stdin: nil, - Stdout: &buf, - Stderr: &buf} + Stdout: &buf} - // cmdModuleLoad.StartWithUi(p.comm, p.ui) p.comm.Start(cmdModuleLoad) cmdModuleLoad.Wait() From 9b1ae530c34c4461402a43f9c7e89b302a6d5440 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 15:35:28 -0800 Subject: [PATCH 8/9] have separate stdout and stderr buffers --- provisioner/windows-restart/provisioner.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 4107fea1b..6d227524a 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -212,16 +212,17 @@ var waitForCommunicator = func(p *Provisioner) error { // provisioning before powershell is actually ready. // In this next check, we parse stdout to make sure that the command is // actually running as expected. - var buf bytes.Buffer + var stdout, stderr bytes.Buffer cmdModuleLoad := &packer.RemoteCmd{ Command: DefaultRestartCheckCommand, Stdin: nil, - Stdout: &buf} + Stdout: &stdout, + Stderr: &stderr} p.comm.Start(cmdModuleLoad) cmdModuleLoad.Wait() - stdoutToRead := buf.String() + stdoutToRead := stdout.String() if !strings.Contains(stdoutToRead, "restarted.") { log.Printf("echo didn't succeed; retrying...") continue From c3cb7fe9f9729ced8725d989e0c7be0ef278aca4 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 9 Nov 2017 15:52:49 -0800 Subject: [PATCH 9/9] read from stderr so it doesnt lock up --- provisioner/windows-restart/provisioner.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 6d227524a..3b03cf116 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -223,7 +223,9 @@ var waitForCommunicator = func(p *Provisioner) error { cmdModuleLoad.Wait() stdoutToRead := stdout.String() + stderrToRead := stderr.String() if !strings.Contains(stdoutToRead, "restarted.") { + log.Printf("Stderr is %s", stderrToRead) log.Printf("echo didn't succeed; retrying...") continue }