move all of the run commands for shell-local provisioner and postprocessor into common library too
This commit is contained in:
parent
c7c66bedcb
commit
6dc4b1cbdc
|
@ -22,8 +22,7 @@ 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 := &sl.Communicator{
|
comm := &sl.Communicator{
|
||||||
Ctx: ctx,
|
ExecuteCommand: []string{command},
|
||||||
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 {
|
||||||
|
|
|
@ -9,12 +9,10 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Communicator struct {
|
type Communicator struct {
|
||||||
ExecuteCommand []string
|
ExecuteCommand []string
|
||||||
Ctx interpolate.Context
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
|
func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
|
||||||
|
@ -24,28 +22,17 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error {
|
||||||
c.ExecuteCommand = []string{
|
c.ExecuteCommand = []string{
|
||||||
"cmd",
|
"cmd",
|
||||||
"/C",
|
"/C",
|
||||||
|
"{{.Vars}}",
|
||||||
"{{.Command}}",
|
"{{.Command}}",
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.ExecuteCommand = []string{
|
c.ExecuteCommand = []string{
|
||||||
"/bin/sh",
|
"/bin/sh",
|
||||||
"-c",
|
"-c",
|
||||||
|
"{{.Vars}}",
|
||||||
"{{.Command}}",
|
"{{.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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the local command to execute
|
// Build the local command to execute
|
||||||
|
@ -97,7 +84,3 @@ func (c *Communicator) Download(string, io.Writer) error {
|
||||||
func (c *Communicator) DownloadDir(string, string, []string) error {
|
func (c *Communicator) DownloadDir(string, string, []string) error {
|
||||||
return fmt.Errorf("downloadDir not supported")
|
return fmt.Errorf("downloadDir not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecuteCommandTemplate struct {
|
|
||||||
Command string
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
package shell_local
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExecuteCommandTemplate struct {
|
||||||
|
Vars string
|
||||||
|
Script string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(ui packer.Ui, config *Config) (bool, error) {
|
||||||
|
scripts := make([]string, len(config.Scripts))
|
||||||
|
copy(scripts, config.Scripts)
|
||||||
|
|
||||||
|
// If we have an inline script, then turn that into a temporary
|
||||||
|
// shell script and use that.
|
||||||
|
if config.Inline != nil {
|
||||||
|
tf, err := ioutil.TempFile("", "packer-shell")
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("Error preparing shell script: %s", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tf.Name())
|
||||||
|
|
||||||
|
// Set the path to the temporary file
|
||||||
|
scripts = append(scripts, tf.Name())
|
||||||
|
|
||||||
|
// Write our contents to it
|
||||||
|
writer := bufio.NewWriter(tf)
|
||||||
|
writer.WriteString(fmt.Sprintf("#!%s\n", config.InlineShebang))
|
||||||
|
for _, command := range config.Inline {
|
||||||
|
if _, err := writer.WriteString(command + "\n"); err != nil {
|
||||||
|
return false, fmt.Errorf("Error preparing shell script: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
return false, fmt.Errorf("Error preparing shell script: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tf.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create environment variables to set before executing the command
|
||||||
|
flattenedEnvVars := createFlattenedEnvVars(config)
|
||||||
|
|
||||||
|
for _, script := range scripts {
|
||||||
|
interpolatedCmds, err := createInterpolatedCommands(config, script, flattenedEnvVars)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
ui.Say(fmt.Sprintf("Post processing with local shell script: %s", script))
|
||||||
|
|
||||||
|
comm := &Communicator{
|
||||||
|
ExecuteCommand: interpolatedCmds,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The remoteCmd generated here isn't actually run, but it allows us to
|
||||||
|
// use the same interafce for the shell-local communicator as we use for
|
||||||
|
// the other communicators; ultimately, this command is just used for
|
||||||
|
// buffers and for reading the final exit status.
|
||||||
|
flattenedCmd := strings.Join(interpolatedCmds, " ")
|
||||||
|
cmd := &packer.RemoteCmd{Command: flattenedCmd}
|
||||||
|
log.Printf("starting local command: %s", flattenedCmd)
|
||||||
|
|
||||||
|
if err := cmd.StartWithUi(comm, ui); err != nil {
|
||||||
|
return false, fmt.Errorf(
|
||||||
|
"Error executing script: %s\n\n"+
|
||||||
|
"Please see output above for more information.",
|
||||||
|
script)
|
||||||
|
}
|
||||||
|
if cmd.ExitStatus != 0 {
|
||||||
|
return false, fmt.Errorf(
|
||||||
|
"Erroneous exit code %d while executing script: %s\n\n"+
|
||||||
|
"Please see output above for more information.",
|
||||||
|
cmd.ExitStatus,
|
||||||
|
script)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates the final command to send to the communicator, using either the
|
||||||
|
// user-provided ExecuteCommand or defaulting to something that makes sense for
|
||||||
|
// the host OS
|
||||||
|
func createInterpolatedCommands(config *Config, script string, flattenedEnvVars string) ([]string, error) {
|
||||||
|
config.Ctx.Data = &ExecuteCommandTemplate{
|
||||||
|
Vars: flattenedEnvVars,
|
||||||
|
Script: script,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(config.ExecuteCommand) == 0 {
|
||||||
|
// Get default Execute Command
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
config.ExecuteCommand = []string{
|
||||||
|
"cmd",
|
||||||
|
"/C",
|
||||||
|
"{{.Vars}}",
|
||||||
|
"{{.Script}}",
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config.ExecuteCommand = []string{
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"{{.Vars}}",
|
||||||
|
"{{.Script}}",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interpolatedCmds := make([]string, len(config.ExecuteCommand))
|
||||||
|
for i, cmd := range config.ExecuteCommand {
|
||||||
|
interpolatedCmd, err := interpolate.Render(cmd, &config.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error processing command: %s", err)
|
||||||
|
}
|
||||||
|
interpolatedCmds[i] = interpolatedCmd
|
||||||
|
}
|
||||||
|
return interpolatedCmds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFlattenedEnvVars(config *Config) (flattened string) {
|
||||||
|
flattened = ""
|
||||||
|
envVars := make(map[string]string)
|
||||||
|
|
||||||
|
// Always available Packer provided env vars
|
||||||
|
envVars["PACKER_BUILD_NAME"] = fmt.Sprintf("%s", config.PackerBuildName)
|
||||||
|
envVars["PACKER_BUILDER_TYPE"] = fmt.Sprintf("%s", config.PackerBuilderType)
|
||||||
|
|
||||||
|
// Split vars into key/value components
|
||||||
|
for _, envVar := range config.Vars {
|
||||||
|
keyValue := strings.SplitN(envVar, "=", 2)
|
||||||
|
// Store pair, replacing any single quotes in value so they parse
|
||||||
|
// correctly with required environment variable format
|
||||||
|
envVars[keyValue[0]] = strings.Replace(keyValue[1], "'", `'"'"'`, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a list of env var keys in sorted order
|
||||||
|
var keys []string
|
||||||
|
for k := range envVars {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
// Re-assemble vars surrounding value with single quotes and flatten
|
||||||
|
for _, key := range keys {
|
||||||
|
flattened += fmt.Sprintf("%s='%s' ", key, envVars[key])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -1,17 +1,8 @@
|
||||||
package shell_local
|
package shell_local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
sl "github.com/hashicorp/packer/common/shell-local"
|
sl "github.com/hashicorp/packer/common/shell-local"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PostProcessor struct {
|
type PostProcessor struct {
|
||||||
|
@ -33,108 +24,13 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
||||||
|
// this particular post-processor doesn't do anything with the artifact
|
||||||
|
// except to return it.
|
||||||
|
|
||||||
scripts := make([]string, len(p.config.Scripts))
|
retBool, retErr := sl.Run(ui, &p.config)
|
||||||
copy(scripts, p.config.Scripts)
|
if !retBool {
|
||||||
|
return nil, retBool, retErr
|
||||||
// If we have an inline script, then turn that into a temporary
|
|
||||||
// shell script and use that.
|
|
||||||
if p.config.Inline != nil {
|
|
||||||
tf, err := ioutil.TempFile("", "packer-shell")
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("Error preparing shell script: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
|
|
||||||
// Set the path to the temporary file
|
|
||||||
scripts = append(scripts, tf.Name())
|
|
||||||
|
|
||||||
// Write our contents to it
|
|
||||||
writer := bufio.NewWriter(tf)
|
|
||||||
writer.WriteString(fmt.Sprintf("#!%s\n", p.config.InlineShebang))
|
|
||||||
for _, command := range p.config.Inline {
|
|
||||||
if _, err := writer.WriteString(command + "\n"); err != nil {
|
|
||||||
return nil, false, fmt.Errorf("Error preparing shell script: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writer.Flush(); err != nil {
|
|
||||||
return nil, false, fmt.Errorf("Error preparing shell script: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tf.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create environment variables to set before executing the command
|
return artifact, retBool, retErr
|
||||||
flattenedEnvVars := p.createFlattenedEnvVars()
|
|
||||||
|
|
||||||
for _, script := range scripts {
|
|
||||||
|
|
||||||
p.config.Ctx.Data = &ExecuteCommandTemplate{
|
|
||||||
Vars: flattenedEnvVars,
|
|
||||||
Script: script,
|
|
||||||
}
|
|
||||||
|
|
||||||
flattenedCmd := strings.Join(p.config.ExecuteCommand, " ")
|
|
||||||
command, err := interpolate.Render(flattenedCmd, &p.config.Ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, false, fmt.Errorf("Error processing command: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf("Post processing with local shell script: %s", script))
|
|
||||||
|
|
||||||
comm := &sl.Communicator{
|
|
||||||
Ctx: p.config.Ctx,
|
|
||||||
ExecuteCommand: []string{flattenedCmd},
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := &packer.RemoteCmd{Command: command}
|
|
||||||
|
|
||||||
log.Printf("starting local command: %s", command)
|
|
||||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
|
||||||
return nil, false, fmt.Errorf(
|
|
||||||
"Error executing script: %s\n\n"+
|
|
||||||
"Please see output above for more information.",
|
|
||||||
script)
|
|
||||||
}
|
|
||||||
if cmd.ExitStatus != 0 {
|
|
||||||
return nil, false, fmt.Errorf(
|
|
||||||
"Erroneous exit code %d while executing script: %s\n\n"+
|
|
||||||
"Please see output above for more information.",
|
|
||||||
cmd.ExitStatus,
|
|
||||||
script)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return artifact, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PostProcessor) createFlattenedEnvVars() (flattened string) {
|
|
||||||
flattened = ""
|
|
||||||
envVars := make(map[string]string)
|
|
||||||
|
|
||||||
// Always available Packer provided env vars
|
|
||||||
envVars["PACKER_BUILD_NAME"] = fmt.Sprintf("%s", p.config.PackerBuildName)
|
|
||||||
envVars["PACKER_BUILDER_TYPE"] = fmt.Sprintf("%s", p.config.PackerBuilderType)
|
|
||||||
|
|
||||||
// Split vars into key/value components
|
|
||||||
for _, envVar := range p.config.Vars {
|
|
||||||
keyValue := strings.SplitN(envVar, "=", 2)
|
|
||||||
// Store pair, replacing any single quotes in value so they parse
|
|
||||||
// correctly with required environment variable format
|
|
||||||
envVars[keyValue[0]] = strings.Replace(keyValue[1], "'", `'"'"'`, -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a list of env var keys in sorted order
|
|
||||||
var keys []string
|
|
||||||
for k := range envVars {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
|
|
||||||
// Re-assemble vars surrounding value with single quotes and flatten
|
|
||||||
for _, key := range keys {
|
|
||||||
flattened += fmt.Sprintf("%s='%s' ", key, envVars[key])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package shell
|
package shell
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
sl "github.com/hashicorp/packer/common/shell-local"
|
sl "github.com/hashicorp/packer/common/shell-local"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
)
|
)
|
||||||
|
@ -21,30 +19,9 @@ 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
|
_, retErr := sl.Run(ui, &p.config)
|
||||||
comm := &sl.Communicator{
|
if retErr != nil {
|
||||||
Ctx: p.config.Ctx,
|
return retErr
|
||||||
ExecuteCommand: p.config.ExecuteCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the remote command
|
|
||||||
cmd := &packer.RemoteCmd{Command: p.config.Command}
|
|
||||||
|
|
||||||
ui.Say(fmt.Sprintf(
|
|
||||||
"Executing local command: %s",
|
|
||||||
p.config.Command))
|
|
||||||
if err := cmd.StartWithUi(comm, ui); err != nil {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Error executing command: %s\n\n"+
|
|
||||||
"Please see output above for more information.",
|
|
||||||
p.config.Command)
|
|
||||||
}
|
|
||||||
if cmd.ExitStatus != 0 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"Erroneous exit code %d while executing command: %s\n\n"+
|
|
||||||
"Please see output above for more information.",
|
|
||||||
cmd.ExitStatus,
|
|
||||||
p.config.Command)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue