deduplicate the nearly identical communicators for the shell-local provisioner and post-processor, moving single communicator into a new common/shell-local module

This commit is contained in:
Megan Marsh 2018-02-23 13:26:31 -08:00
parent 23e51d21c2
commit 616b41e58f
7 changed files with 41 additions and 143 deletions

View File

@ -3,8 +3,8 @@ package chroot
import ( import (
"fmt" "fmt"
sl "github.com/hashicorp/packer/common/shell-local"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/post-processor/shell-local"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
) )
@ -21,7 +21,10 @@ func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ctx inte
} }
ui.Say(fmt.Sprintf("Executing command: %s", command)) ui.Say(fmt.Sprintf("Executing command: %s", command))
comm := &shell_local.Communicator{} comm := &sl.Communicator{
Ctx: ctx,
ExecuteCommand: []string{""},
}
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}
if err := cmd.StartWithUi(comm, ui); err != nil { if err := cmd.StartWithUi(comm, ui); err != nil {
return fmt.Errorf("Error executing command: %s", err) return fmt.Errorf("Error executing command: %s", err)

View File

@ -1,10 +1,11 @@
package shell package shell_local
import ( import (
"fmt" "fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
"runtime"
"syscall" "syscall"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
@ -17,17 +18,34 @@ type Communicator struct {
} }
func (c *Communicator) Start(cmd *packer.RemoteCmd) error { func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
// Render the template so that we know how to execute the command if len(c.ExecuteCommand) == 0 {
c.Ctx.Data = &ExecuteCommandTemplate{ // Get default Execute Command
Command: cmd.Command, if runtime.GOOS == "windows" {
} c.ExecuteCommand = []string{
for i, field := range c.ExecuteCommand { "cmd",
command, err := interpolate.Render(field, &c.Ctx) "/C",
if err != nil { "{{.Command}}",
return fmt.Errorf("Error processing command: %s", err) }
} else {
c.ExecuteCommand = []string{
"/bin/sh",
"-c",
"{{.Command}}",
}
} }
} else {
// Render the template so that we know how to execute the command
c.Ctx.Data = &ExecuteCommandTemplate{
Command: cmd.Command,
}
for i, field := range c.ExecuteCommand {
command, err := interpolate.Render(field, &c.Ctx)
if err != nil {
return fmt.Errorf("Error processing command: %s", err)
}
c.ExecuteCommand[i] = command c.ExecuteCommand[i] = command
}
} }
// Build the local command to execute // Build the local command to execute

View File

@ -1,4 +1,4 @@
package shell package shell_local
import ( import (
"bytes" "bytes"

View File

@ -1,63 +0,0 @@
package shell_local
import (
"fmt"
"io"
"os"
"os/exec"
"syscall"
"github.com/hashicorp/packer/packer"
)
type Communicator struct{}
func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
localCmd := exec.Command("sh", "-c", cmd.Command)
localCmd.Stdin = cmd.Stdin
localCmd.Stdout = cmd.Stdout
localCmd.Stderr = cmd.Stderr
// Start it. If it doesn't work, then error right away.
if err := localCmd.Start(); err != nil {
return err
}
// We've started successfully. Start a goroutine to wait for
// it to complete and track exit status.
go func() {
var exitStatus int
err := localCmd.Wait()
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
exitStatus = 1
// There is no process-independent way to get the REAL
// exit status so we just try to go deeper.
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
exitStatus = status.ExitStatus()
}
}
}
cmd.SetExited(exitStatus)
}()
return nil
}
func (c *Communicator) Upload(string, io.Reader, *os.FileInfo) error {
return fmt.Errorf("upload not supported")
}
func (c *Communicator) UploadDir(string, string, []string) error {
return fmt.Errorf("uploadDir not supported")
}
func (c *Communicator) Download(string, io.Writer) error {
return fmt.Errorf("download not supported")
}
func (c *Communicator) DownloadDir(src string, dst string, exclude []string) error {
return fmt.Errorf("downloadDir not supported")
}

View File

@ -1,43 +0,0 @@
package shell_local
import (
"bytes"
"runtime"
"strings"
"testing"
"github.com/hashicorp/packer/packer"
)
func TestCommunicator_impl(t *testing.T) {
var _ packer.Communicator = new(Communicator)
}
func TestCommunicator(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("windows not supported for this test")
return
}
c := &Communicator{}
var buf bytes.Buffer
cmd := &packer.RemoteCmd{
Command: "/bin/echo foo",
Stdout: &buf,
}
if err := c.Start(cmd); err != nil {
t.Fatalf("err: %s", err)
}
cmd.Wait()
if cmd.ExitStatus != 0 {
t.Fatalf("err bad exit status: %d", cmd.ExitStatus)
}
if strings.TrimSpace(buf.String()) != "foo" {
t.Fatalf("bad: %s", buf.String())
}
}

View File

@ -11,6 +11,7 @@ import (
"strings" "strings"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
sl "github.com/hashicorp/packer/common/shell-local"
"github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
@ -178,7 +179,10 @@ func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (pac
ui.Say(fmt.Sprintf("Post processing with local shell script: %s", script)) ui.Say(fmt.Sprintf("Post processing with local shell script: %s", script))
comm := &Communicator{} comm := &sl.Communicator{
Ctx: p.config.ctx,
ExecuteCommand: []string{p.config.ExecuteCommand},
}
cmd := &packer.RemoteCmd{Command: command} cmd := &packer.RemoteCmd{Command: command}

View File

@ -3,9 +3,9 @@ package shell
import ( import (
"errors" "errors"
"fmt" "fmt"
"runtime"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
sl "github.com/hashicorp/packer/common/shell-local"
"github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate" "github.com/hashicorp/packer/template/interpolate"
@ -41,33 +41,12 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
return err return err
} }
if len(p.config.ExecuteCommand) == 0 {
if runtime.GOOS == "windows" {
p.config.ExecuteCommand = []string{
"cmd",
"/C",
"{{.Command}}",
}
} else {
p.config.ExecuteCommand = []string{
"/bin/sh",
"-c",
"{{.Command}}",
}
}
}
var errs *packer.MultiError var errs *packer.MultiError
if p.config.Command == "" { if p.config.Command == "" {
errs = packer.MultiErrorAppend(errs, errs = packer.MultiErrorAppend(errs,
errors.New("command must be specified")) errors.New("command must be specified"))
} }
if len(p.config.ExecuteCommand) == 0 {
errs = packer.MultiErrorAppend(errs,
errors.New("execute_command must not be empty"))
}
if errs != nil && len(errs.Errors) > 0 { if errs != nil && len(errs.Errors) > 0 {
return errs return errs
} }
@ -77,7 +56,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
func (p *Provisioner) Provision(ui packer.Ui, _ packer.Communicator) error { func (p *Provisioner) Provision(ui packer.Ui, _ packer.Communicator) error {
// Make another communicator for local // Make another communicator for local
comm := &Communicator{ comm := &sl.Communicator{
Ctx: p.config.ctx, Ctx: p.config.ctx,
ExecuteCommand: p.config.ExecuteCommand, ExecuteCommand: p.config.ExecuteCommand,
} }