allow a provisioner to timeout

* I had to contextualise Communicator.Start and RemoteCmd.StartWithUi
NOTE: Communicator.Start starts a RemoteCmd but RemoteCmd.StartWithUi will run the cmd and wait for a return, so I renamed StartWithUi to RunWithUi so that the intent is clearer.
Ideally in the future RunWithUi will be named back to StartWithUi and the exit status or wait funcs of the command will allow to wait for a return. If you do so please read carrefully https://golang.org/pkg/os/exec/#Cmd.Stdout to avoid a deadlock
* cmd.ExitStatus to cmd.ExitStatus() is now blocking to avoid race conditions
* also had to simplify StartWithUi
This commit is contained in:
Adrien Delorme 2019-04-03 17:14:55 +02:00
parent f7cd2b9334
commit f555e7a9f2
49 changed files with 292 additions and 245 deletions

View File

@ -2,6 +2,7 @@ package chroot
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -23,7 +24,7 @@ type Communicator struct {
CmdWrapper CommandWrapper CmdWrapper CommandWrapper
} }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) error {
// need extra escapes for the command since we're wrapping it in quotes // need extra escapes for the command since we're wrapping it in quotes
cmd.Command = strconv.Quote(cmd.Command) cmd.Command = strconv.Quote(cmd.Command)
command, err := c.CmdWrapper( command, err := c.CmdWrapper(

View File

@ -1,6 +1,7 @@
package chroot package chroot
import ( import (
"context"
"fmt" "fmt"
sl "github.com/hashicorp/packer/common/shell-local" sl "github.com/hashicorp/packer/common/shell-local"
@ -9,6 +10,7 @@ import (
) )
func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ictx interpolate.Context, ui packer.Ui) error { func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ictx interpolate.Context, ui packer.Ui) error {
ctx := context.TODO()
for _, rawCmd := range commands { for _, rawCmd := range commands {
intCmd, err := interpolate.Render(rawCmd, &ictx) intCmd, err := interpolate.Render(rawCmd, &ictx)
if err != nil { if err != nil {
@ -25,13 +27,13 @@ func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ictx int
ExecuteCommand: []string{"sh", "-c", command}, ExecuteCommand: []string{"sh", "-c", command},
} }
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Error executing command: %s", err) return fmt.Errorf("Error executing command: %s", err)
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf( return fmt.Errorf(
"Received non-zero exit code %d from command: %s", "Received non-zero exit code %d from command: %s",
cmd.ExitStatus, cmd.ExitStatus(),
command) command)
} }
} }

View File

@ -59,13 +59,13 @@ func (s *StepBundleVolume) Run(ctx context.Context, state multistep.StateBag) mu
ui.Say(fmt.Sprintf("Running: %s", config.BundleVolCommand)) ui.Say(fmt.Sprintf("Running: %s", config.BundleVolCommand))
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
state.Put("error", fmt.Errorf("Error bundling volume: %s", err)) state.Put("error", fmt.Errorf("Error bundling volume: %s", err))
ui.Error(state.Get("error").(error).Error()) ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
state.Put("error", fmt.Errorf( state.Put("error", fmt.Errorf(
"Volume bundling failed. Please see the output above for more\n"+ "Volume bundling failed. Please see the output above for more\n"+
"details on what went wrong.\n\n"+ "details on what went wrong.\n\n"+

View File

@ -69,14 +69,14 @@ func (s *StepUploadBundle) Run(ctx context.Context, state multistep.StateBag) mu
ui.Say(fmt.Sprintf("Running: %s", config.BundleUploadCommand)) ui.Say(fmt.Sprintf("Running: %s", config.BundleUploadCommand))
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
state.Put("error", fmt.Errorf("Error uploading volume: %s", err)) state.Put("error", fmt.Errorf("Error uploading volume: %s", err))
ui.Error(state.Get("error").(error).Error()) ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
if cmd.ExitStatus == 3 { if cmd.ExitStatus() == 3 {
ui.Error(fmt.Sprintf("Please check that the bucket `%s` "+ ui.Error(fmt.Sprintf("Please check that the bucket `%s` "+
"does not exist, or exists and is writable. This error "+ "does not exist, or exists and is writable. This error "+
"indicates that the bucket may be owned by somebody else.", "indicates that the bucket may be owned by somebody else.",

View File

@ -2,6 +2,7 @@ package docker
import ( import (
"archive/tar" "archive/tar"
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -28,7 +29,9 @@ type Communicator struct {
EntryPoint []string EntryPoint []string
} }
func (c *Communicator) Start(remote *packer.RemoteCmd) error { var _ packer.Communicator = new(Communicator)
func (c *Communicator) Start(ctx context.Context, remote *packer.RemoteCmd) error {
dockerArgs := []string{ dockerArgs := []string{
"exec", "exec",
"-i", "-i",

View File

@ -15,10 +15,6 @@ import (
"github.com/hashicorp/packer/template" "github.com/hashicorp/packer/template"
) )
func TestCommunicator_impl(t *testing.T) {
var _ packer.Communicator = new(Communicator)
}
// TestUploadDownload verifies that basic upload / download functionality works // TestUploadDownload verifies that basic upload / download functionality works
func TestUploadDownload(t *testing.T) { func TestUploadDownload(t *testing.T) {
ui := packer.TestUi(t) ui := packer.TestUi(t)

View File

@ -1,6 +1,7 @@
package hyperone package hyperone
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -20,7 +21,7 @@ type ChrootCommunicator struct {
Wrapped packer.Communicator Wrapped packer.Communicator
} }
func (c *ChrootCommunicator) Start(cmd *packer.RemoteCmd) error { func (c *ChrootCommunicator) Start(ctx context.Context, cmd *packer.RemoteCmd) error {
command := strconv.Quote(cmd.Command) command := strconv.Quote(cmd.Command)
chrootCommand, err := c.CmdWrapper( chrootCommand, err := c.CmdWrapper(
fmt.Sprintf("sudo chroot %s /bin/sh -c %s", c.Chroot, command)) fmt.Sprintf("sudo chroot %s /bin/sh -c %s", c.Chroot, command))
@ -30,7 +31,7 @@ func (c *ChrootCommunicator) Start(cmd *packer.RemoteCmd) error {
cmd.Command = chrootCommand cmd.Command = chrootCommand
return c.Wrapped.Start(cmd) return c.Wrapped.Start(ctx, cmd)
} }
func (c *ChrootCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { func (c *ChrootCommunicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {

View File

@ -2,6 +2,7 @@ package hyperone
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"log" "log"
"strings" "strings"
@ -22,6 +23,7 @@ func formatOpenAPIError(err error) string {
} }
func runCommands(commands []string, ictx interpolate.Context, state multistep.StateBag) error { func runCommands(commands []string, ictx interpolate.Context, state multistep.StateBag) error {
ctx := context.TODO()
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
wrappedCommand := state.Get("wrappedCommand").(CommandWrapper) wrappedCommand := state.Get("wrappedCommand").(CommandWrapper)
comm := state.Get("communicator").(packer.Communicator) comm := state.Get("communicator").(packer.Communicator)
@ -43,15 +45,15 @@ func runCommands(commands []string, ictx interpolate.Context, state multistep.St
ui.Say(fmt.Sprintf("Executing command: %s", command)) ui.Say(fmt.Sprintf("Executing command: %s", command))
err = remoteCmd.StartWithUi(comm, ui) err = remoteCmd.RunWithUi(ctx, comm, ui)
if err != nil { if err != nil {
return fmt.Errorf("error running remote cmd: %s", err) return fmt.Errorf("error running remote cmd: %s", err)
} }
if remoteCmd.ExitStatus != 0 { if remoteCmd.ExitStatus() != 0 {
return fmt.Errorf( return fmt.Errorf(
"received non-zero exit code %d from command: %s", "received non-zero exit code %d from command: %s",
remoteCmd.ExitStatus, remoteCmd.ExitStatus(),
command) command)
} }
} }
@ -59,6 +61,7 @@ func runCommands(commands []string, ictx interpolate.Context, state multistep.St
} }
func captureOutput(command string, state multistep.StateBag) (string, error) { func captureOutput(command string, state multistep.StateBag) (string, error) {
ctx := context.TODO()
comm := state.Get("communicator").(packer.Communicator) comm := state.Get("communicator").(packer.Communicator)
var stdout bytes.Buffer var stdout bytes.Buffer
@ -69,16 +72,16 @@ func captureOutput(command string, state multistep.StateBag) (string, error) {
log.Println(fmt.Sprintf("Executing command: %s", command)) log.Println(fmt.Sprintf("Executing command: %s", command))
err := comm.Start(remoteCmd) err := comm.Start(ctx, remoteCmd)
if err != nil { if err != nil {
return "", fmt.Errorf("error running remote cmd: %s", err) return "", fmt.Errorf("error running remote cmd: %s", err)
} }
remoteCmd.Wait() remoteCmd.Wait()
if remoteCmd.ExitStatus != 0 { if remoteCmd.ExitStatus() != 0 {
return "", fmt.Errorf( return "", fmt.Errorf(
"received non-zero exit code %d from command: %s", "received non-zero exit code %d from command: %s",
remoteCmd.ExitStatus, remoteCmd.ExitStatus(),
command) command)
} }

View File

@ -45,7 +45,7 @@ func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
Stdout: &stdout, Stdout: &stdout,
Stderr: &stderr, Stderr: &stderr,
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err) err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())

View File

@ -1,6 +1,7 @@
package lxc package lxc
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -22,7 +23,7 @@ type LxcAttachCommunicator struct {
CmdWrapper CommandWrapper CmdWrapper CommandWrapper
} }
func (c *LxcAttachCommunicator) Start(cmd *packer.RemoteCmd) error { func (c *LxcAttachCommunicator) Start(ctx context.Context, cmd *packer.RemoteCmd) error {
localCmd, err := c.Execute(cmd.Command) localCmd, err := c.Execute(cmd.Command)
if err != nil { if err != nil {

View File

@ -1,6 +1,7 @@
package lxd package lxd
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -17,7 +18,7 @@ type Communicator struct {
CmdWrapper CommandWrapper CmdWrapper CommandWrapper
} }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) error {
localCmd, err := c.Execute(cmd.Command) localCmd, err := c.Execute(cmd.Command)
if err != nil { if err != nil {
@ -55,11 +56,13 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
} }
func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error { func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {
ctx := context.TODO()
fileDestination := filepath.Join(c.ContainerName, dst) fileDestination := filepath.Join(c.ContainerName, dst)
// find out if the place we are pushing to is a directory // find out if the place we are pushing to is a directory
testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst) testDirectoryCommand := fmt.Sprintf(`test -d "%s"`, dst)
cmd := &packer.RemoteCmd{Command: testDirectoryCommand} cmd := &packer.RemoteCmd{Command: testDirectoryCommand}
err := c.Start(cmd) err := c.Start(ctx, cmd)
if err != nil { if err != nil {
log.Printf("Unable to check whether remote path is a dir: %s", err) log.Printf("Unable to check whether remote path is a dir: %s", err)
@ -67,7 +70,7 @@ func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {
} }
cmd.Wait() cmd.Wait()
if cmd.ExitStatus == 0 { if cmd.ExitStatus() == 0 {
log.Printf("path is a directory; copying file into directory.") log.Printf("path is a directory; copying file into directory.")
fileDestination = filepath.Join(c.ContainerName, dst, (*fi).Name()) fileDestination = filepath.Join(c.ContainerName, dst, (*fi).Name())
} }

View File

@ -66,15 +66,15 @@ func (s *stepUploadImage) Run(ctx context.Context, state multistep.StateBag) mul
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("sudo /bin/sh %s", dest), Command: fmt.Sprintf("sudo /bin/sh %s", dest),
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
err = fmt.Errorf("Problem creating image`: %s", err) err = fmt.Errorf("Problem creating image`: %s", err)
ui.Error(err.Error()) ui.Error(err.Error())
state.Put("error", err) state.Put("error", err)
return multistep.ActionHalt return multistep.ActionHalt
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
err = fmt.Errorf("Create Disk Image command failed with exit code %d", cmd.ExitStatus) err = fmt.Errorf("Create Disk Image command failed with exit code %d", cmd.ExitStatus())
ui.Error(err.Error()) ui.Error(err.Error())
state.Put("error", err) state.Put("error", err)
return multistep.ActionHalt return multistep.ActionHalt

View File

@ -45,7 +45,7 @@ func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
Stdout: &stdout, Stdout: &stdout,
Stderr: &stderr, Stderr: &stderr,
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err) err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())

View File

@ -52,7 +52,7 @@ func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
ui.Say("Gracefully halting virtual machine...") ui.Say("Gracefully halting virtual machine...")
log.Printf("Executing shutdown command: %s", config.ShutdownCommand) log.Printf("Executing shutdown command: %s", config.ShutdownCommand)
cmd := &packer.RemoteCmd{Command: config.ShutdownCommand} cmd := &packer.RemoteCmd{Command: config.ShutdownCommand}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err) err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())

View File

@ -38,7 +38,7 @@ func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
ui.Say("Gracefully halting virtual machine...") ui.Say("Gracefully halting virtual machine...")
log.Printf("Executing shutdown command: %s", s.Command) log.Printf("Executing shutdown command: %s", s.Command)
cmd := &packer.RemoteCmd{Command: s.Command} cmd := &packer.RemoteCmd{Command: s.Command}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err) err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())

View File

@ -687,6 +687,7 @@ func (d *ESX5Driver) VerifyChecksum(ctype string, hash string, file string) bool
} }
func (d *ESX5Driver) ssh(command string, stdin io.Reader) (*bytes.Buffer, error) { func (d *ESX5Driver) ssh(command string, stdin io.Reader) (*bytes.Buffer, error) {
ctx := context.TODO()
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
@ -696,14 +697,14 @@ func (d *ESX5Driver) ssh(command string, stdin io.Reader) (*bytes.Buffer, error)
Stdin: stdin, Stdin: stdin,
} }
err := d.comm.Start(cmd) err := d.comm.Start(ctx, cmd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
cmd.Wait() cmd.Wait()
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
err = fmt.Errorf("'%s'\n\nStdout: %s\n\nStderr: %s", err = fmt.Errorf("'%s'\n\nStdout: %s\n\nStderr: %s",
cmd.Command, stdout.String(), stderr.String()) cmd.Command, stdout.String(), stderr.String())
return nil, err return nil, err

View File

@ -51,7 +51,7 @@ func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
Stdout: &stdout, Stdout: &stdout,
Stderr: &stderr, Stderr: &stderr,
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err) err := fmt.Errorf("Failed to send shutdown command: %s", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())

View File

@ -2,6 +2,7 @@ package adapter
import ( import (
"bytes" "bytes"
"context"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -233,15 +234,15 @@ func (c *Adapter) remoteExec(command string, in io.Reader, out io.Writer, err io
Stderr: err, Stderr: err,
Command: command, Command: command,
} }
ctx := context.TODO()
if err := c.comm.Start(cmd); err != nil { if err := c.comm.Start(ctx, cmd); err != nil {
c.ui.Error(err.Error()) c.ui.Error(err.Error())
return cmd.ExitStatus
} }
cmd.Wait() cmd.Wait()
return cmd.ExitStatus return cmd.ExitStatus()
} }
type envRequest struct { type envRequest struct {

View File

@ -1,6 +1,7 @@
package adapter package adapter
import ( import (
"context"
"errors" "errors"
"io" "io"
"log" "log"
@ -95,7 +96,7 @@ func (a addr) String() string {
type communicator struct{} type communicator struct{}
func (c communicator) Start(*packer.RemoteCmd) error { func (c communicator) Start(context.Context, *packer.RemoteCmd) error {
return errors.New("communicator not supported") return errors.New("communicator not supported")
} }

View File

@ -1,6 +1,7 @@
package shell_local package shell_local
import ( import (
"context"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -15,14 +16,14 @@ type Communicator struct {
ExecuteCommand []string ExecuteCommand []string
} }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) error {
if len(c.ExecuteCommand) == 0 { if len(c.ExecuteCommand) == 0 {
return fmt.Errorf("Error launching command via shell-local communicator: No ExecuteCommand provided") return fmt.Errorf("Error launching command via shell-local communicator: No ExecuteCommand provided")
} }
// Build the local command to execute // Build the local command to execute
log.Printf("[INFO] (shell-local communicator): Executing local shell command %s", c.ExecuteCommand) log.Printf("[INFO] (shell-local communicator): Executing local shell command %s", c.ExecuteCommand)
localCmd := exec.Command(c.ExecuteCommand[0], c.ExecuteCommand[1:]...) localCmd := exec.CommandContext(ctx, c.ExecuteCommand[0], c.ExecuteCommand[1:]...)
localCmd.Stdin = cmd.Stdin localCmd.Stdin = cmd.Stdin
localCmd.Stdout = cmd.Stdout localCmd.Stdout = cmd.Stdout
localCmd.Stderr = cmd.Stderr localCmd.Stderr = cmd.Stderr

View File

@ -2,6 +2,7 @@ package shell_local
import ( import (
"bytes" "bytes"
"context"
"runtime" "runtime"
"strings" "strings"
"testing" "testing"
@ -28,14 +29,15 @@ func TestCommunicator(t *testing.T) {
Stdout: &buf, Stdout: &buf,
} }
if err := c.Start(cmd); err != nil { ctx := context.Background()
if err := c.Start(ctx, cmd); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
cmd.Wait() cmd.Wait()
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
t.Fatalf("err bad exit status: %d", cmd.ExitStatus) t.Fatalf("err bad exit status: %d", cmd.ExitStatus())
} }
if strings.TrimSpace(buf.String()) != "foo" { if strings.TrimSpace(buf.String()) != "foo" {

View File

@ -2,6 +2,7 @@ package shell_local
import ( import (
"bufio" "bufio"
"context"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -27,7 +28,7 @@ type EnvVarsTemplate struct {
WinRMPassword string WinRMPassword string
} }
func Run(ui packer.Ui, config *Config) (bool, error) { func Run(ctx context.Context, ui packer.Ui, config *Config) (bool, error) {
// Check if shell-local can even execute against this runtime OS // Check if shell-local can even execute against this runtime OS
if len(config.OnlyOn) > 0 { if len(config.OnlyOn) > 0 {
runCommand := false runCommand := false
@ -90,17 +91,17 @@ func Run(ui packer.Ui, config *Config) (bool, error) {
flattenedCmd := strings.Join(interpolatedCmds, " ") flattenedCmd := strings.Join(interpolatedCmds, " ")
cmd := &packer.RemoteCmd{Command: flattenedCmd} cmd := &packer.RemoteCmd{Command: flattenedCmd}
log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd) log.Printf("[INFO] (shell-local): starting local command: %s", flattenedCmd)
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return false, fmt.Errorf( return false, fmt.Errorf(
"Error executing script: %s\n\n"+ "Error executing script: %s\n\n"+
"Please see output above for more information.", "Please see output above for more information.",
script) script)
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return false, fmt.Errorf( return false, fmt.Errorf(
"Erroneous exit code %d while executing script: %s\n\n"+ "Erroneous exit code %d while executing script: %s\n\n"+
"Please see output above for more information.", "Please see output above for more information.",
cmd.ExitStatus, cmd.ExitStatus(),
script) script)
} }
} }

View File

@ -56,12 +56,12 @@ func (s *StepCleanupTempKeys) Run(ctx context.Context, state multistep.StateBag)
// //
// TODO: Why create a backup file if you are going to remove it? // TODO: Why create a backup file if you are going to remove it?
cmd.Command = fmt.Sprintf("sed -i.bak '/ %s$/d' ~/.ssh/authorized_keys; rm ~/.ssh/authorized_keys.bak", s.Comm.SSHTemporaryKeyPairName) cmd.Command = fmt.Sprintf("sed -i.bak '/ %s$/d' ~/.ssh/authorized_keys; rm ~/.ssh/authorized_keys.bak", s.Comm.SSHTemporaryKeyPairName)
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
log.Printf("Error cleaning up ~/.ssh/authorized_keys; please clean up keys manually: %s", err) log.Printf("Error cleaning up ~/.ssh/authorized_keys; please clean up keys manually: %s", err)
} }
cmd = new(packer.RemoteCmd) cmd = new(packer.RemoteCmd)
cmd.Command = fmt.Sprintf("sudo sed -i.bak '/ %s$/d' /root/.ssh/authorized_keys; sudo rm /root/.ssh/authorized_keys.bak", s.Comm.SSHTemporaryKeyPairName) cmd.Command = fmt.Sprintf("sudo sed -i.bak '/ %s$/d' /root/.ssh/authorized_keys; sudo rm /root/.ssh/authorized_keys.bak", s.Comm.SSHTemporaryKeyPairName)
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
log.Printf("Error cleaning up /root/.ssh/authorized_keys; please clean up keys manually: %s", err) log.Printf("Error cleaning up /root/.ssh/authorized_keys; please clean up keys manually: %s", err)
} }

View File

@ -1,6 +1,7 @@
package none package none
import ( import (
"context"
"errors" "errors"
"io" "io"
"os" "os"
@ -23,7 +24,7 @@ func New(config string) (result *comm, err error) {
return return
} }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { func (c *comm) Start(ctx context.Context, cmd *packer.RemoteCmd) (err error) {
cmd.SetExited(0) cmd.SetExited(0)
return return
} }

View File

@ -3,6 +3,7 @@ package ssh
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -82,7 +83,7 @@ func New(address string, config *Config) (result *comm, err error) {
return return
} }
func (c *comm) Start(cmd *packer.RemoteCmd) (err error) { func (c *comm) Start(ctx context.Context, cmd *packer.RemoteCmd) (err error) {
session, err := c.newSession() session, err := c.newSession()
if err != nil { if err != nil {
return return

View File

@ -4,6 +4,7 @@ package ssh
import ( import (
"bytes" "bytes"
"context"
"fmt" "fmt"
"net" "net"
"testing" "testing"
@ -188,7 +189,8 @@ func TestStart(t *testing.T) {
Stdout: new(bytes.Buffer), Stdout: new(bytes.Buffer),
} }
client.Start(cmd) ctx := context.Background()
client.Start(ctx, cmd)
} }
func TestHandshakeTimeout(t *testing.T) { func TestHandshakeTimeout(t *testing.T) {

View File

@ -1,6 +1,7 @@
package winrm package winrm
import ( import (
"context"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io" "io"
@ -74,7 +75,7 @@ func New(config *Config) (*Communicator, error) {
} }
// Start implementation of communicator.Communicator interface // Start implementation of communicator.Communicator interface
func (c *Communicator) Start(rc *packer.RemoteCmd) error { func (c *Communicator) Start(ctx context.Context, rc *packer.RemoteCmd) error {
shell, err := c.client.CreateShell() shell, err := c.client.CreateShell()
if err != nil { if err != nil {
return err return err

View File

@ -2,6 +2,7 @@ package winrm
import ( import (
"bytes" "bytes"
"context"
"io" "io"
"strings" "strings"
"testing" "testing"
@ -77,8 +78,8 @@ func TestStart(t *testing.T) {
stdout := new(bytes.Buffer) stdout := new(bytes.Buffer)
cmd.Command = "echo foo" cmd.Command = "echo foo"
cmd.Stdout = stdout cmd.Stdout = stdout
ctx := context.Background()
err = c.Start(&cmd) err = c.Start(ctx, &cmd)
if err != nil { if err != nil {
t.Fatalf("error executing remote command: %s", err) t.Fatalf("error executing remote command: %s", err)
} }

View File

@ -33,7 +33,7 @@ type StepConnectWinRM struct {
WinRMPort func(multistep.StateBag) (int, error) WinRMPort func(multistep.StateBag) (int, error)
} }
func (s *StepConnectWinRM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *StepConnectWinRM) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
var comm packer.Communicator var comm packer.Communicator
@ -87,6 +87,7 @@ func (s *StepConnectWinRM) Cleanup(multistep.StateBag) {
} }
func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) { func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
ctx := context.TODO()
var comm packer.Communicator var comm packer.Communicator
for { for {
select { select {
@ -164,7 +165,7 @@ func (s *StepConnectWinRM) waitForWinRM(state multistep.StateBag, cancel <-chan
log.Printf("Checking that WinRM is connected with: '%s'", connectCheckCommand) log.Printf("Checking that WinRM is connected with: '%s'", connectCheckCommand)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
err := cmd.StartWithUi(comm, ui) err := cmd.RunWithUi(ctx, comm, ui)
if err != nil { if err != nil {
log.Printf("Communication connection err: %s", err) log.Printf("Communication connection err: %s", err)

View File

@ -1,12 +1,15 @@
package packer package packer
import ( import (
"context"
"io" "io"
"os" "os"
"strings" "strings"
"sync" "sync"
"unicode" "unicode"
"golang.org/x/sync/errgroup"
"github.com/mitchellh/iochan" "github.com/mitchellh/iochan"
) )
@ -32,19 +35,14 @@ type RemoteCmd struct {
Stdout io.Writer Stdout io.Writer
Stderr io.Writer Stderr io.Writer
// This will be set to true when the remote command has exited. It
// shouldn't be set manually by the user, but there is no harm in
// doing so.
Exited bool
// Once Exited is true, this will contain the exit code of the process. // Once Exited is true, this will contain the exit code of the process.
ExitStatus int exitStatus int
// Internal fields
exitCh chan struct{}
// This thing is a mutex, lock when making modifications concurrently // This thing is a mutex, lock when making modifications concurrently
sync.Mutex sync.Mutex
exitChInit sync.Once
exitCh chan interface{}
} }
// A Communicator is the interface used to communicate with the machine // A Communicator is the interface used to communicate with the machine
@ -59,7 +57,7 @@ type Communicator interface {
// Start again. The Start method returns immediately once the command // Start again. The Start method returns immediately once the command
// is started. It does not wait for the command to complete. The // is started. It does not wait for the command to complete. The
// RemoteCmd.Exited field should be used for this. // RemoteCmd.Exited field should be used for this.
Start(*RemoteCmd) error Start(context.Context, *RemoteCmd) error
// Upload uploads a file to the machine to the given path with the // Upload uploads a file to the machine to the given path with the
// contents coming from the given reader. This method will block until // contents coming from the given reader. This method will block until
@ -84,10 +82,12 @@ type Communicator interface {
DownloadDir(src string, dst string, exclude []string) error DownloadDir(src string, dst string, exclude []string) error
} }
// StartWithUi runs the remote command and streams the output to any // RunWithUi runs the remote command and streams the output to any configured
// configured Writers for stdout/stderr, while also writing each line // Writers for stdout/stderr, while also writing each line as it comes to a Ui.
// as it comes to a Ui. // RunWithUi will not return until the command finishes or is cancelled.
func (r *RemoteCmd) StartWithUi(c Communicator, ui Ui) error { func (r *RemoteCmd) RunWithUi(ctx context.Context, c Communicator, ui Ui) error {
r.initchan()
stdout_r, stdout_w := io.Pipe() stdout_r, stdout_w := io.Pipe()
stderr_r, stderr_w := io.Pipe() stderr_r, stderr_w := io.Pipe()
defer stdout_w.Close() defer stdout_w.Close()
@ -117,85 +117,69 @@ func (r *RemoteCmd) StartWithUi(c Communicator, ui Ui) error {
r.Stderr = io.MultiWriter(r.Stderr, stderr_w) r.Stderr = io.MultiWriter(r.Stderr, stderr_w)
} }
// Start the command // Loop and get all our output until done.
if err := c.Start(r); err != nil { printFn := func(in io.Reader, out func(string)) error {
for output := range iochan.DelimReader(in, '\n') {
if output != "" {
out(cleanOutputLine(output))
}
}
return nil
}
wg, ctx := errgroup.WithContext(ctx)
wg.Go(func() error { return printFn(stdout_r, ui.Message) })
wg.Go(func() error { return printFn(stderr_r, ui.Error) })
if err := c.Start(ctx, r); err != nil {
return err return err
} }
// Create the channels we'll use for data
exitCh := make(chan struct{})
stdoutCh := iochan.DelimReader(stdout_r, '\n')
stderrCh := iochan.DelimReader(stderr_r, '\n')
// Start the goroutine to watch for the exit
go func() {
defer close(exitCh)
defer stdout_w.Close()
defer stderr_w.Close()
r.Wait()
}()
// Loop and get all our output
OutputLoop:
for {
select { select {
case output := <-stderrCh: case <-ctx.Done():
if output != "" { return ctx.Err()
ui.Message(r.cleanOutputLine(output)) case <-r.exitCh:
}
case output := <-stdoutCh:
if output != "" {
ui.Message(r.cleanOutputLine(output))
}
case <-exitCh:
break OutputLoop
}
}
// Make sure we finish off stdout/stderr because we may have gotten
// a message from the exit channel before finishing these first.
for output := range stdoutCh {
ui.Message(r.cleanOutputLine(output))
}
for output := range stderrCh {
ui.Message(r.cleanOutputLine(output))
}
return nil return nil
} }
}
// SetExited is a helper for setting that this process is exited. This // SetExited is a helper for setting that this process is exited. This
// should be called by communicators who are running a remote command in // should be called by communicators who are running a remote command in
// order to set that the command is done. // order to set that the command is done.
func (r *RemoteCmd) SetExited(status int) { func (r *RemoteCmd) SetExited(status int) {
r.initchan()
r.Lock() r.Lock()
defer r.Unlock() r.exitStatus = status
r.Unlock()
if r.exitCh == nil {
r.exitCh = make(chan struct{})
}
r.Exited = true
r.ExitStatus = status
close(r.exitCh) close(r.exitCh)
} }
// Wait waits for the remote command to complete. // Wait for command exit and return exit status
func (r *RemoteCmd) Wait() { func (r *RemoteCmd) Wait() int {
// Make sure our condition variable is initialized. r.initchan()
r.Lock()
if r.exitCh == nil {
r.exitCh = make(chan struct{})
}
r.Unlock()
<-r.exitCh <-r.exitCh
r.Lock()
defer r.Unlock()
return r.exitStatus
}
func (r *RemoteCmd) ExitStatus() int {
return r.Wait()
}
func (r *RemoteCmd) initchan() {
r.exitChInit.Do(func() {
if r.exitCh == nil {
r.exitCh = make(chan interface{})
}
})
} }
// cleanOutputLine cleans up a line so that '\r' don't muck up the // cleanOutputLine cleans up a line so that '\r' don't muck up the
// UI output when we're reading from a remote command. // UI output when we're reading from a remote command.
func (r *RemoteCmd) cleanOutputLine(line string) string { func cleanOutputLine(line string) string {
// Trim surrounding whitespace // Trim surrounding whitespace
line = strings.TrimRightFunc(line, unicode.IsSpace) line = strings.TrimRightFunc(line, unicode.IsSpace)

View File

@ -2,6 +2,7 @@ package packer
import ( import (
"bytes" "bytes"
"context"
"io" "io"
"os" "os"
"sync" "sync"
@ -34,7 +35,7 @@ type MockCommunicator struct {
DownloadData string DownloadData string
} }
func (c *MockCommunicator) Start(rc *RemoteCmd) error { func (c *MockCommunicator) Start(ctx context.Context, rc *RemoteCmd) error {
c.StartCalled = true c.StartCalled = true
c.StartCmd = rc c.StartCmd = rc

View File

@ -2,9 +2,12 @@ package packer
import ( import (
"bytes" "bytes"
"context"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/google/go-cmp/cmp"
) )
func TestRemoteCmd_StartWithUi(t *testing.T) { func TestRemoteCmd_StartWithUi(t *testing.T) {
@ -24,17 +27,19 @@ func TestRemoteCmd_StartWithUi(t *testing.T) {
Command: "test", Command: "test",
Stdout: originalOutput, Stdout: originalOutput,
} }
ctx := context.TODO()
err := rc.StartWithUi(testComm, testUi) err := rc.RunWithUi(ctx, testComm, testUi)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
rc.Wait() // sometimes cmd has returned and everything can be printed later on
time.Sleep(1 * time.Second)
expected := strings.TrimSpace(data) expected := strings.TrimSpace(data)
if strings.TrimSpace(uiOutput.String()) != expected { if diff := cmp.Diff(strings.TrimSpace(uiOutput.String()), expected); diff != "" {
t.Fatalf("bad output: '%s'", uiOutput.String()) t.Fatalf("bad output: %s", diff)
} }
if originalOutput.String() != expected { if originalOutput.String() != expected {

View File

@ -16,10 +16,10 @@ const HookProvision = "packer_provision"
// in. In addition to that, the Hook is given access to a UI so that it can // in. In addition to that, the Hook is given access to a UI so that it can
// output things to the user. // output things to the user.
// //
// Cancel is called when the hook needs to be cancelled. This will usually // The first context argument controlls cancellation, the context will usually
// be called when Run is still in progress so the mechanism that handles this // be called when Run is still in progress so the mechanism that handles this
// must be race-free. Cancel should attempt to cancel the hook in the // must be race-free. Cancel should attempt to cancel the hook in the quickest,
// quickest, safest way possible. // safest way possible.
type Hook interface { type Hook interface {
Run(context.Context, string, Ui, Communicator, interface{}) error Run(context.Context, string, Ui, Communicator, interface{}) error
} }

View File

@ -1,6 +1,7 @@
package rpc package rpc
import ( import (
"context"
"encoding/gob" "encoding/gob"
"io" "io"
"log" "log"
@ -64,7 +65,7 @@ func Communicator(client *rpc.Client) *communicator {
return &communicator{client: client} return &communicator{client: client}
} }
func (c *communicator) Start(cmd *packer.RemoteCmd) (err error) { func (c *communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) (err error) {
var args CommunicatorStartArgs var args CommunicatorStartArgs
args.Command = cmd.Command args.Command = cmd.Command
@ -201,6 +202,8 @@ func (c *communicator) Download(path string, w io.Writer) (err error) {
} }
func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface{}) error { func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface{}) error {
ctx := context.TODO()
// Build the RemoteCmd on this side so that it all pipes over // Build the RemoteCmd on this side so that it all pipes over
// to the remote side. // to the remote side.
var cmd packer.RemoteCmd var cmd packer.RemoteCmd
@ -260,7 +263,7 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
responseWriter := gob.NewEncoder(responseC) responseWriter := gob.NewEncoder(responseC)
// Start the actual command // Start the actual command
err = c.c.Start(&cmd) err = c.c.Start(ctx, &cmd)
if err != nil { if err != nil {
close(doneCh) close(doneCh)
return NewBasicError(err) return NewBasicError(err)
@ -272,8 +275,8 @@ func (c *CommunicatorServer) Start(args *CommunicatorStartArgs, reply *interface
defer close(doneCh) defer close(doneCh)
defer responseC.Close() defer responseC.Close()
cmd.Wait() cmd.Wait()
log.Printf("[INFO] RPC endpoint: Communicator ended with: %d", cmd.ExitStatus) log.Printf("[INFO] RPC endpoint: Communicator ended with: %d", cmd.ExitStatus())
responseWriter.Encode(&CommandFinished{cmd.ExitStatus}) responseWriter.Encode(&CommandFinished{cmd.ExitStatus()})
}() }()
return nil return nil

View File

@ -2,6 +2,7 @@ package rpc
import ( import (
"bufio" "bufio"
"context"
"io" "io"
"reflect" "reflect"
"testing" "testing"
@ -36,8 +37,10 @@ func TestCommunicatorRPC(t *testing.T) {
c.StartStderr = "errfoo\n" c.StartStderr = "errfoo\n"
c.StartExitStatus = 42 c.StartExitStatus = 42
ctx := context.Background()
// Test Start // Test Start
err := remote.Start(&cmd) err := remote.Start(ctx, &cmd)
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -73,8 +76,8 @@ func TestCommunicatorRPC(t *testing.T) {
} }
// Test that we can get the exit status properly // Test that we can get the exit status properly
if cmd.ExitStatus != 42 { if cmd.ExitStatus() != 42 {
t.Fatalf("bad exit: %d", cmd.ExitStatus) t.Fatalf("bad exit: %d", cmd.ExitStatus())
} }
// Test that we can upload things // Test that we can upload things

View File

@ -41,7 +41,7 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact
// this particular post-processor doesn't do anything with the artifact // this particular post-processor doesn't do anything with the artifact
// except to return it. // except to return it.
success, retErr := sl.Run(ui, &p.config) success, retErr := sl.Run(ctx, ui, &p.config)
if !success { if !success {
return nil, false, false, retErr return nil, false, false, retErr
} }

View File

@ -1,6 +1,7 @@
package ansiblelocal package ansiblelocal
import ( import (
"context"
"io" "io"
"os" "os"
@ -12,7 +13,7 @@ type communicatorMock struct {
uploadDestination []string uploadDestination []string
} }
func (c *communicatorMock) Start(cmd *packer.RemoteCmd) error { func (c *communicatorMock) Start(ctx context.Context, cmd *packer.RemoteCmd) error {
c.startCommand = append(c.startCommand, cmd.Command) c.startCommand = append(c.startCommand, cmd.Command)
cmd.SetExited(0) cmd.SetExited(0)
return nil return nil

View File

@ -348,6 +348,7 @@ func (p *Provisioner) provisionPlaybookFile(ui packer.Ui, comm packer.Communicat
} }
func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error { func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error {
ctx := context.TODO()
rolesDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles")) rolesDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles"))
galaxyFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.GalaxyFile))) galaxyFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.GalaxyFile)))
@ -358,12 +359,12 @@ func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) erro
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: command, Command: command,
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
// ansible-galaxy version 2.0.0.2 doesn't return exit codes on error.. // ansible-galaxy version 2.0.0.2 doesn't return exit codes on error..
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus())
} }
return nil return nil
} }
@ -403,6 +404,7 @@ func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) err
func (p *Provisioner) executeAnsiblePlaybook( func (p *Provisioner) executeAnsiblePlaybook(
ui packer.Ui, comm packer.Communicator, playbookFile, extraArgs, inventory string, ui packer.Ui, comm packer.Communicator, playbookFile, extraArgs, inventory string,
) error { ) error {
ctx := context.TODO()
command := fmt.Sprintf("cd %s && %s %s%s -c local -i %s", command := fmt.Sprintf("cd %s && %s %s%s -c local -i %s",
p.config.StagingDir, p.config.Command, playbookFile, extraArgs, inventory, p.config.StagingDir, p.config.Command, playbookFile, extraArgs, inventory,
) )
@ -410,17 +412,17 @@ func (p *Provisioner) executeAnsiblePlaybook(
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: command, Command: command,
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
if cmd.ExitStatus == 127 { if cmd.ExitStatus() == 127 {
return fmt.Errorf("%s could not be found. Verify that it is available on the\n"+ return fmt.Errorf("%s could not be found. Verify that it is available on the\n"+
"PATH after connecting to the machine.", "PATH after connecting to the machine.",
p.config.Command) p.config.Command)
} }
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus())
} }
return nil return nil
} }
@ -464,32 +466,34 @@ func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, sr
} }
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ctx := context.TODO()
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("mkdir -p '%s'", dir), Command: fmt.Sprintf("mkdir -p '%s'", dir),
} }
ui.Message(fmt.Sprintf("Creating directory: %s", dir)) ui.Message(fmt.Sprintf("Creating directory: %s", dir))
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more information.") return fmt.Errorf("Non-zero exit status. See output above for more information.")
} }
return nil return nil
} }
func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ctx := context.TODO()
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("rm -rf '%s'", dir), Command: fmt.Sprintf("rm -rf '%s'", dir),
} }
ui.Message(fmt.Sprintf("Removing directory: %s", dir)) ui.Message(fmt.Sprintf("Removing directory: %s", dir))
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more information.") return fmt.Errorf("Non-zero exit status. See output above for more information.")
} }
return nil return nil

View File

@ -467,22 +467,23 @@ func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string
} }
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Creating directory: %s", dir)) ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
// Chmod the directory to 0777 just so that we can access it as our user // Chmod the directory to 0777 just so that we can access it as our user
cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")} cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
@ -514,6 +515,7 @@ func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node str
"-y", "-y",
"-c", knifeConfigPath, "-c", knifeConfigPath,
} }
ctx := context.TODO()
p.config.ctx.Data = &KnifeTemplate{ p.config.ctx.Data = &KnifeTemplate{
Sudo: !p.config.PreventSudo, Sudo: !p.config.PreventSudo,
@ -527,10 +529,10 @@ func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node str
} }
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf( return fmt.Errorf(
"Non-zero exit status. See output above for more info.\n\n"+ "Non-zero exit status. See output above for more info.\n\n"+
"Command: %s", "Command: %s",
@ -542,9 +544,10 @@ func (p *Provisioner) knifeExec(ui packer.Ui, comm packer.Communicator, node str
func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Removing directory: %s", dir)) ui.Message(fmt.Sprintf("Removing directory: %s", dir))
ctx := context.TODO()
cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
@ -557,6 +560,8 @@ func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config
JsonPath: json, JsonPath: json,
Sudo: !p.config.PreventSudo, Sudo: !p.config.PreventSudo,
} }
ctx := context.TODO()
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
if err != nil { if err != nil {
return err return err
@ -575,12 +580,12 @@ func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config
Command: command, Command: command,
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus())
} }
return nil return nil
@ -588,6 +593,7 @@ func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config
func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error { func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error {
ui.Message("Installing Chef...") ui.Message("Installing Chef...")
ctx := context.TODO()
p.config.ctx.Data = &InstallChefTemplate{ p.config.ctx.Data = &InstallChefTemplate{
Sudo: !p.config.PreventSudo, Sudo: !p.config.PreventSudo,
@ -600,13 +606,13 @@ func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator) error
ui.Message(command) ui.Message(command)
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf( return fmt.Errorf(
"Install script exited with non-zero exit status %d", cmd.ExitStatus) "Install script exited with non-zero exit status %d", cmd.ExitStatus())
} }
return nil return nil

View File

@ -410,21 +410,22 @@ func (p *Provisioner) createJson(ui packer.Ui, comm packer.Communicator) (string
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Creating directory: %s", dir)) ui.Message(fmt.Sprintf("Creating directory: %s", dir))
ctx := context.TODO()
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
// Chmod the directory to 0777 just so that we can access it as our user // Chmod the directory to 0777 just so that we can access it as our user
cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")} cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
@ -447,13 +448,13 @@ func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: command, Command: command,
} }
ctx := context.TODO()
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus())
} }
return nil return nil
@ -461,6 +462,7 @@ func (p *Provisioner) executeChef(ui packer.Ui, comm packer.Communicator, config
func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator, version string) error { func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator, version string) error {
ui.Message("Installing Chef...") ui.Message("Installing Chef...")
ctx := context.TODO()
p.config.ctx.Data = &InstallChefTemplate{ p.config.ctx.Data = &InstallChefTemplate{
Sudo: !p.config.PreventSudo, Sudo: !p.config.PreventSudo,
@ -472,13 +474,13 @@ func (p *Provisioner) installChef(ui packer.Ui, comm packer.Communicator, versio
} }
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf( return fmt.Errorf(
"Install script exited with non-zero exit status %d", cmd.ExitStatus) "Install script exited with non-zero exit status %d", cmd.ExitStatus())
} }
return nil return nil

View File

@ -128,6 +128,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
} }
func (p *Provisioner) maybeBootstrap(ui packer.Ui, comm packer.Communicator) error { func (p *Provisioner) maybeBootstrap(ui packer.Ui, comm packer.Communicator) error {
ctx := context.TODO()
if !p.config.Bootstrap { if !p.config.Bootstrap {
return nil return nil
} }
@ -153,12 +154,12 @@ func (p *Provisioner) maybeBootstrap(ui packer.Ui, comm packer.Communicator) err
Stderr: &outErr, Stderr: &outErr,
} }
if err = comm.Start(cmd); err != nil { if err = comm.Start(ctx, cmd); err != nil {
return fmt.Errorf("Error bootstrapping converge: %s", err) return fmt.Errorf("Error bootstrapping converge: %s", err)
} }
cmd.Wait() cmd.Wait()
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
ui.Error(out.String()) ui.Error(out.String())
ui.Error(outErr.String()) ui.Error(outErr.String())
return errors.New("Error bootstrapping converge") return errors.New("Error bootstrapping converge")
@ -180,6 +181,7 @@ func (p *Provisioner) sendModuleDirectories(ui packer.Ui, comm packer.Communicat
} }
func (p *Provisioner) applyModules(ui packer.Ui, comm packer.Communicator) error { func (p *Provisioner) applyModules(ui packer.Ui, comm packer.Communicator) error {
ctx := context.TODO()
// create params JSON file // create params JSON file
params, err := json.Marshal(p.config.Params) params, err := json.Marshal(p.config.Params)
if err != nil { if err != nil {
@ -208,12 +210,12 @@ func (p *Provisioner) applyModules(ui packer.Ui, comm packer.Communicator) error
Stdout: &runOut, Stdout: &runOut,
Stderr: &runErr, Stderr: &runErr,
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
return fmt.Errorf("Error applying %q: %s", p.config.Module, err) return fmt.Errorf("Error applying %q: %s", p.config.Module, err)
} }
cmd.Wait() cmd.Wait()
if cmd.ExitStatus == 127 { if cmd.ExitStatus() == 127 {
ui.Error("Could not find Converge. Is it installed and in PATH?") ui.Error("Could not find Converge. Is it installed and in PATH?")
if !p.config.Bootstrap { if !p.config.Bootstrap {
ui.Error("Bootstrapping was disabled for this run. That might be why Converge isn't present.") ui.Error("Bootstrapping was disabled for this run. That might be why Converge isn't present.")
@ -221,10 +223,10 @@ func (p *Provisioner) applyModules(ui packer.Ui, comm packer.Communicator) error
return errors.New("Could not find Converge") return errors.New("Could not find Converge")
} else if cmd.ExitStatus != 0 { } else if cmd.ExitStatus() != 0 {
ui.Error(strings.TrimSpace(runOut.String())) ui.Error(strings.TrimSpace(runOut.String()))
ui.Error(strings.TrimSpace(runErr.String())) ui.Error(strings.TrimSpace(runErr.String()))
ui.Error(fmt.Sprintf("Exited with error code %d.", cmd.ExitStatus)) ui.Error(fmt.Sprintf("Exited with error code %d.", cmd.ExitStatus()))
return fmt.Errorf("Error applying %q", p.config.Module) return fmt.Errorf("Error applying %q", p.config.Module)
} }

View File

@ -262,7 +262,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
} }
cmd = &packer.RemoteCmd{Command: command} cmd = &packer.RemoteCmd{Command: command}
return cmd.StartWithUi(comm, ui) return cmd.RunWithUi(ctx, comm, ui)
}) })
if err != nil { if err != nil {
return err return err
@ -271,7 +271,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
// Close the original file since we copied it // Close the original file since we copied it
f.Close() f.Close()
if err := p.config.ValidExitCode(cmd.ExitStatus); err != nil { if err := p.config.ValidExitCode(cmd.ExitStatus()); err != nil {
return err return err
} }
} }

View File

@ -348,12 +348,12 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
} }
ui.Message(fmt.Sprintf("Running Puppet: %s", command)) ui.Message(fmt.Sprintf("Running Puppet: %s", command))
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Got an error starting command: %s", err) return fmt.Errorf("Got an error starting command: %s", err)
} }
if cmd.ExitStatus != 0 && cmd.ExitStatus != 2 && !p.config.IgnoreExitCodes { if cmd.ExitStatus() != 0 && cmd.ExitStatus() != 2 && !p.config.IgnoreExitCodes {
return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus())
} }
if p.config.CleanStagingDir { if p.config.CleanStagingDir {
@ -431,21 +431,22 @@ func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir stri
ui.Message(fmt.Sprintf("Creating directory: %s", dir)) ui.Message(fmt.Sprintf("Creating directory: %s", dir))
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
ctx := context.TODO()
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status.")
} }
// Chmod the directory to 0777 just so that we can access it as our user // Chmod the directory to 0777 just so that we can access it as our user
cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")} cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
@ -453,12 +454,14 @@ func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir stri
} }
func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ctx := context.TODO()
cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status.")
} }

View File

@ -301,12 +301,12 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
} }
ui.Message(fmt.Sprintf("Running Puppet: %s", command)) ui.Message(fmt.Sprintf("Running Puppet: %s", command))
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 && cmd.ExitStatus != 2 && !p.config.IgnoreExitCodes { if cmd.ExitStatus() != 0 && cmd.ExitStatus() != 2 && !p.config.IgnoreExitCodes {
return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Puppet exited with a non-zero exit status: %d", cmd.ExitStatus())
} }
if p.config.CleanStagingDir { if p.config.CleanStagingDir {
@ -320,21 +320,22 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ui.Message(fmt.Sprintf("Creating directory: %s", dir)) ui.Message(fmt.Sprintf("Creating directory: %s", dir))
ctx := context.TODO()
cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.CreateDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
// Chmod the directory to 0777 just so that we can access it as our user // Chmod the directory to 0777 just so that we can access it as our user
cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")} cmd = &packer.RemoteCmd{Command: p.guestCommands.Chmod(dir, "0777")}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status. See output above for more info.") return fmt.Errorf("Non-zero exit status. See output above for more info.")
} }
@ -342,12 +343,14 @@ func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir stri
} }
func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ctx := context.TODO()
cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)} cmd := &packer.RemoteCmd{Command: p.guestCommands.RemoveDir(dir)}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status.")
} }

View File

@ -231,14 +231,14 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
Command: fmt.Sprintf(p.guestOSTypeConfig.bootstrapFetchCmd), Command: fmt.Sprintf(p.guestOSTypeConfig.bootstrapFetchCmd),
} }
ui.Message(fmt.Sprintf("Downloading saltstack bootstrap to /tmp/install_salt.sh")) ui.Message(fmt.Sprintf("Downloading saltstack bootstrap to /tmp/install_salt.sh"))
if err = cmd.StartWithUi(comm, ui); err != nil { if err = cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Unable to download Salt: %s", err) return fmt.Errorf("Unable to download Salt: %s", err)
} }
cmd = &packer.RemoteCmd{ cmd = &packer.RemoteCmd{
Command: fmt.Sprintf("%s %s", p.sudo(p.guestOSTypeConfig.bootstrapRunCmd), p.config.BootstrapArgs), Command: fmt.Sprintf("%s %s", p.sudo(p.guestOSTypeConfig.bootstrapRunCmd), p.config.BootstrapArgs),
} }
ui.Message(fmt.Sprintf("Installing Salt with command %s", cmd.Command)) ui.Message(fmt.Sprintf("Installing Salt with command %s", cmd.Command))
if err = cmd.StartWithUi(comm, ui); err != nil { if err = cmd.RunWithUi(ctx, comm, ui); err != nil {
return fmt.Errorf("Unable to install Salt: %s", err) return fmt.Errorf("Unable to install Salt: %s", err)
} }
} }
@ -342,9 +342,9 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
ui.Message(fmt.Sprintf("Running: salt-call --local %s", p.config.CmdArgs)) ui.Message(fmt.Sprintf("Running: salt-call --local %s", p.config.CmdArgs))
cmd := &packer.RemoteCmd{Command: p.sudo(fmt.Sprintf("%s --local %s", filepath.Join(p.config.SaltBinDir, "salt-call"), p.config.CmdArgs))} cmd := &packer.RemoteCmd{Command: p.sudo(fmt.Sprintf("%s --local %s", filepath.Join(p.config.SaltBinDir, "salt-call"), p.config.CmdArgs))}
if err = cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 { if err = cmd.RunWithUi(ctx, comm, ui); err != nil || cmd.ExitStatus() != 0 {
if err == nil { if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus) err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus())
} }
return fmt.Errorf("Error executing salt-call: %s", err) return fmt.Errorf("Error executing salt-call: %s", err)
@ -406,13 +406,15 @@ func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, sr
} }
func (p *Provisioner) moveFile(ui packer.Ui, comm packer.Communicator, dst string, src string) error { func (p *Provisioner) moveFile(ui packer.Ui, comm packer.Communicator, dst string, src string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Moving %s to %s", src, dst)) ui.Message(fmt.Sprintf("Moving %s to %s", src, dst))
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: p.sudo(p.guestCommands.MovePath(src, dst)), Command: p.sudo(p.guestCommands.MovePath(src, dst)),
} }
if err := cmd.StartWithUi(comm, ui); err != nil || cmd.ExitStatus != 0 { if err := cmd.RunWithUi(ctx, comm, ui); err != nil || cmd.ExitStatus() != 0 {
if err == nil { if err == nil {
err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus) err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus())
} }
return fmt.Errorf("Unable to move %s to %s: %s", src, dst, err) return fmt.Errorf("Unable to move %s to %s: %s", src, dst, err)
@ -425,38 +427,41 @@ func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir stri
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: p.guestCommands.CreateDir(dir), Command: p.guestCommands.CreateDir(dir),
} }
if err := cmd.StartWithUi(comm, ui); err != nil { ctx := context.TODO()
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status.")
} }
return nil return nil
} }
func (p *Provisioner) statPath(ui packer.Ui, comm packer.Communicator, path string) error { func (p *Provisioner) statPath(ui packer.Ui, comm packer.Communicator, path string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Verifying Path: %s", path)) ui.Message(fmt.Sprintf("Verifying Path: %s", path))
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: p.guestCommands.StatPath(path), Command: p.guestCommands.StatPath(path),
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status.")
} }
return nil return nil
} }
func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error { func (p *Provisioner) removeDir(ui packer.Ui, comm packer.Communicator, dir string) error {
ctx := context.TODO()
ui.Message(fmt.Sprintf("Removing directory: %s", dir)) ui.Message(fmt.Sprintf("Removing directory: %s", dir))
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: p.guestCommands.RemoveDir(dir), Command: p.guestCommands.RemoveDir(dir),
} }
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
return err return err
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf("Non-zero exit status.") return fmt.Errorf("Non-zero exit status.")
} }
return nil return nil

View File

@ -26,7 +26,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
} }
func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator) error { func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, _ packer.Communicator) error {
_, retErr := sl.Run(ui, &p.config) _, retErr := sl.Run(ctx, ui, &p.config)
if retErr != nil { if retErr != nil {
return retErr return retErr
} }

View File

@ -256,7 +256,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
cmd = &packer.RemoteCmd{ cmd = &packer.RemoteCmd{
Command: fmt.Sprintf("chmod 0600 %s", remoteVFName), Command: fmt.Sprintf("chmod 0600 %s", remoteVFName),
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error chmodding script file to 0600 in remote "+ "Error chmodding script file to 0600 in remote "+
"machine: %s", err) "machine: %s", err)
@ -314,7 +314,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
cmd = &packer.RemoteCmd{ cmd = &packer.RemoteCmd{
Command: fmt.Sprintf("chmod 0755 %s", p.config.RemotePath), Command: fmt.Sprintf("chmod 0755 %s", p.config.RemotePath),
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error chmodding script file to 0755 in remote "+ "Error chmodding script file to 0755 in remote "+
"machine: %s", err) "machine: %s", err)
@ -322,7 +322,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
cmd.Wait() cmd.Wait()
cmd = &packer.RemoteCmd{Command: command} cmd = &packer.RemoteCmd{Command: command}
return cmd.StartWithUi(comm, ui) return cmd.RunWithUi(ctx, comm, ui)
}) })
if err != nil { if err != nil {
@ -331,7 +331,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
// If the exit code indicates a remote disconnect, fail unless // If the exit code indicates a remote disconnect, fail unless
// we were expecting it. // we were expecting it.
if cmd.ExitStatus == packer.CmdDisconnect { if cmd.ExitStatus() == packer.CmdDisconnect {
if !p.config.ExpectDisconnect { if !p.config.ExpectDisconnect {
return fmt.Errorf("Script disconnected unexpectedly. " + return fmt.Errorf("Script disconnected unexpectedly. " +
"If you expected your script to disconnect, i.e. from a " + "If you expected your script to disconnect, i.e. from a " +
@ -339,7 +339,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
"or `\"valid_exit_codes\": [0, 2300218]` to the shell " + "or `\"valid_exit_codes\": [0, 2300218]` to the shell " +
"provisioner parameters.") "provisioner parameters.")
} }
} else if err := p.config.ValidExitCode(cmd.ExitStatus); err != nil { } else if err := p.config.ValidExitCode(cmd.ExitStatus()); err != nil {
return err return err
} }
@ -371,21 +371,22 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
} }
func (p *Provisioner) cleanupRemoteFile(path string, comm packer.Communicator) error { func (p *Provisioner) cleanupRemoteFile(path string, comm packer.Communicator) error {
ctx := context.TODO()
err := p.retryable(func() error { err := p.retryable(func() error {
cmd := &packer.RemoteCmd{ cmd := &packer.RemoteCmd{
Command: fmt.Sprintf("rm -f %s", path), Command: fmt.Sprintf("rm -f %s", path),
} }
if err := comm.Start(cmd); err != nil { if err := comm.Start(ctx, cmd); err != nil {
return fmt.Errorf( return fmt.Errorf(
"Error removing temporary script at %s: %s", "Error removing temporary script at %s: %s",
path, err) path, err)
} }
cmd.Wait() cmd.Wait()
// treat disconnects as retryable by returning an error // treat disconnects as retryable by returning an error
if cmd.ExitStatus == packer.CmdDisconnect { if cmd.ExitStatus() == packer.CmdDisconnect {
return fmt.Errorf("Disconnect while removing temporary script.") return fmt.Errorf("Disconnect while removing temporary script.")
} }
if cmd.ExitStatus != 0 { if cmd.ExitStatus() != 0 {
return fmt.Errorf( return fmt.Errorf(
"Error removing temporary script at %s!", "Error removing temporary script at %s!",
path) path)

View File

@ -106,15 +106,15 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
command := p.config.RestartCommand command := p.config.RestartCommand
err := p.retryable(func() error { err := p.retryable(func() error {
cmd = &packer.RemoteCmd{Command: command} cmd = &packer.RemoteCmd{Command: command}
return cmd.StartWithUi(comm, ui) return cmd.RunWithUi(ctx, comm, ui)
}) })
if err != nil { if err != nil {
return err return err
} }
if cmd.ExitStatus != 0 && cmd.ExitStatus != 1115 && cmd.ExitStatus != 1190 { if cmd.ExitStatus() != 0 && cmd.ExitStatus() != 1115 && cmd.ExitStatus() != 1190 {
return fmt.Errorf("Restart script exited with non-zero exit status: %d", cmd.ExitStatus) return fmt.Errorf("Restart script exited with non-zero exit status: %d", cmd.ExitStatus())
} }
return waitForRestart(ctx, p, comm) return waitForRestart(ctx, p, comm)
@ -141,25 +141,25 @@ var waitForRestart = func(ctx context.Context, p *Provisioner, comm packer.Commu
for { for {
log.Printf("Check if machine is rebooting...") log.Printf("Check if machine is rebooting...")
cmd = &packer.RemoteCmd{Command: trycommand} cmd = &packer.RemoteCmd{Command: trycommand}
err = cmd.StartWithUi(comm, ui) err = cmd.RunWithUi(ctx, comm, ui)
if err != nil { if err != nil {
// Couldn't execute, we assume machine is rebooting already // Couldn't execute, we assume machine is rebooting already
break break
} }
if cmd.ExitStatus == 1 { if cmd.ExitStatus() == 1 {
// SSH provisioner, and we're already rebooting. SSH can reconnect // SSH provisioner, and we're already rebooting. SSH can reconnect
// without our help; exit this wait loop. // without our help; exit this wait loop.
break break
} }
if cmd.ExitStatus == 1115 || cmd.ExitStatus == 1190 || cmd.ExitStatus == 1717 { if cmd.ExitStatus() == 1115 || cmd.ExitStatus() == 1190 || cmd.ExitStatus() == 1717 {
// Reboot already in progress but not completed // Reboot already in progress but not completed
log.Printf("Reboot already in progress, waiting...") log.Printf("Reboot already in progress, waiting...")
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
} }
if cmd.ExitStatus == 0 { if cmd.ExitStatus() == 0 {
// Cancel reboot we created to test if machine was already rebooting // Cancel reboot we created to test if machine was already rebooting
cmd = &packer.RemoteCmd{Command: abortcommand} cmd = &packer.RemoteCmd{Command: abortcommand}
cmd.StartWithUi(comm, ui) cmd.RunWithUi(ctx, comm, ui)
break break
} }
} }
@ -221,7 +221,7 @@ var waitForCommunicator = func(ctx context.Context, p *Provisioner) error {
} }
if runCustomRestartCheck { if runCustomRestartCheck {
// run user-configured restart check // run user-configured restart check
err := cmdRestartCheck.StartWithUi(p.comm, p.ui) err := cmdRestartCheck.RunWithUi(ctx, p.comm, p.ui)
if err != nil { if err != nil {
log.Printf("Communication connection err: %s", err) log.Printf("Communication connection err: %s", err)
continue continue
@ -243,7 +243,7 @@ var waitForCommunicator = func(ctx context.Context, p *Provisioner) error {
cmdModuleLoad.Stdout = &buf cmdModuleLoad.Stdout = &buf
cmdModuleLoad.Stdout = io.MultiWriter(cmdModuleLoad.Stdout, &buf2) cmdModuleLoad.Stdout = io.MultiWriter(cmdModuleLoad.Stdout, &buf2)
cmdModuleLoad.StartWithUi(p.comm, p.ui) cmdModuleLoad.RunWithUi(ctx, p.comm, p.ui)
stdoutToRead := buf2.String() stdoutToRead := buf2.String()
if !strings.Contains(stdoutToRead, "restarted.") { if !strings.Contains(stdoutToRead, "restarted.") {
@ -262,7 +262,7 @@ var waitForCommunicator = func(ctx context.Context, p *Provisioner) error {
cmdKeyCheck.Stdout = &buf cmdKeyCheck.Stdout = &buf
cmdKeyCheck.Stdout = io.MultiWriter(cmdKeyCheck.Stdout, &buf2) cmdKeyCheck.Stdout = io.MultiWriter(cmdKeyCheck.Stdout, &buf2)
err := p.comm.Start(cmdKeyCheck) err := p.comm.Start(ctx, cmdKeyCheck)
if err != nil { if err != nil {
log.Printf("Communication connection err: %s", err) log.Printf("Communication connection err: %s", err)
shouldContinue = true shouldContinue = true

View File

@ -214,7 +214,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
} }
cmd = &packer.RemoteCmd{Command: command} cmd = &packer.RemoteCmd{Command: command}
return cmd.StartWithUi(comm, ui) return cmd.RunWithUi(ctx, comm, ui)
}) })
if err != nil { if err != nil {
return err return err
@ -223,7 +223,7 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
// Close the original file since we copied it // Close the original file since we copied it
f.Close() f.Close()
if err := p.config.ValidExitCode(cmd.ExitStatus); err != nil { if err := p.config.ValidExitCode(cmd.ExitStatus()); err != nil {
return err return err
} }
} }