github.com/aspring/packer@v0.8.1-0.20150629211158-9db281ac0f89/provisioner/shell-local/communicator.go (about)

     1  package shell
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"syscall"
     9  
    10  	"github.com/mitchellh/packer/packer"
    11  	"github.com/mitchellh/packer/template/interpolate"
    12  )
    13  
    14  type Communicator struct {
    15  	ExecuteCommand []string
    16  	Ctx            interpolate.Context
    17  }
    18  
    19  func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
    20  	// Render the template so that we know how to execute the command
    21  	c.Ctx.Data = &ExecuteCommandTemplate{
    22  		Command: cmd.Command,
    23  	}
    24  	for i, field := range c.ExecuteCommand {
    25  		command, err := interpolate.Render(field, &c.Ctx)
    26  		if err != nil {
    27  			return fmt.Errorf("Error processing command: %s", err)
    28  		}
    29  
    30  		c.ExecuteCommand[i] = command
    31  	}
    32  
    33  	// Build the local command to execute
    34  	localCmd := exec.Command(c.ExecuteCommand[0], c.ExecuteCommand[1:]...)
    35  	localCmd.Stdin = cmd.Stdin
    36  	localCmd.Stdout = cmd.Stdout
    37  	localCmd.Stderr = cmd.Stderr
    38  
    39  	// Start it. If it doesn't work, then error right away.
    40  	if err := localCmd.Start(); err != nil {
    41  		return err
    42  	}
    43  
    44  	// We've started successfully. Start a goroutine to wait for
    45  	// it to complete and track exit status.
    46  	go func() {
    47  		var exitStatus int
    48  		err := localCmd.Wait()
    49  		if err != nil {
    50  			if exitErr, ok := err.(*exec.ExitError); ok {
    51  				exitStatus = 1
    52  
    53  				// There is no process-independent way to get the REAL
    54  				// exit status so we just try to go deeper.
    55  				if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
    56  					exitStatus = status.ExitStatus()
    57  				}
    58  			}
    59  		}
    60  
    61  		cmd.SetExited(exitStatus)
    62  	}()
    63  
    64  	return nil
    65  }
    66  
    67  func (c *Communicator) Upload(string, io.Reader, *os.FileInfo) error {
    68  	return fmt.Errorf("upload not supported")
    69  }
    70  
    71  func (c *Communicator) UploadDir(string, string, []string) error {
    72  	return fmt.Errorf("uploadDir not supported")
    73  }
    74  
    75  func (c *Communicator) Download(string, io.Writer) error {
    76  	return fmt.Errorf("download not supported")
    77  }
    78  
    79  type ExecuteCommandTemplate struct {
    80  	Command string
    81  }