From 6dc4b1cbdc1be33ff953d11a1b4643f366f05ae0 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 28 Feb 2018 11:53:53 -0800 Subject: [PATCH] move all of the run commands for shell-local provisioner and postprocessor into common library too --- builder/amazon/chroot/run_local_commands.go | 3 +- common/shell-local/communicator.go | 21 +-- common/shell-local/run.go | 160 +++++++++++++++++++ post-processor/shell-local/post-processor.go | 116 +------------- provisioner/shell-local/provisioner.go | 29 +--- 5 files changed, 172 insertions(+), 157 deletions(-) create mode 100644 common/shell-local/run.go diff --git a/builder/amazon/chroot/run_local_commands.go b/builder/amazon/chroot/run_local_commands.go index 154d37a4f..4d5b0f75c 100644 --- a/builder/amazon/chroot/run_local_commands.go +++ b/builder/amazon/chroot/run_local_commands.go @@ -22,8 +22,7 @@ func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ctx inte ui.Say(fmt.Sprintf("Executing command: %s", command)) comm := &sl.Communicator{ - Ctx: ctx, - ExecuteCommand: []string{""}, + ExecuteCommand: []string{command}, } cmd := &packer.RemoteCmd{Command: command} if err := cmd.StartWithUi(comm, ui); err != nil { diff --git a/common/shell-local/communicator.go b/common/shell-local/communicator.go index dc84b575a..5532143c9 100644 --- a/common/shell-local/communicator.go +++ b/common/shell-local/communicator.go @@ -9,12 +9,10 @@ import ( "syscall" "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template/interpolate" ) type Communicator struct { ExecuteCommand []string - Ctx interpolate.Context } func (c *Communicator) Start(cmd *packer.RemoteCmd) error { @@ -24,28 +22,17 @@ func (c *Communicator) Start(cmd *packer.RemoteCmd) error { c.ExecuteCommand = []string{ "cmd", "/C", + "{{.Vars}}", "{{.Command}}", } } else { c.ExecuteCommand = []string{ "/bin/sh", "-c", + "{{.Vars}}", "{{.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 @@ -97,7 +84,3 @@ func (c *Communicator) Download(string, io.Writer) error { func (c *Communicator) DownloadDir(string, string, []string) error { return fmt.Errorf("downloadDir not supported") } - -type ExecuteCommandTemplate struct { - Command string -} diff --git a/common/shell-local/run.go b/common/shell-local/run.go new file mode 100644 index 000000000..a42cb3216 --- /dev/null +++ b/common/shell-local/run.go @@ -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 +} diff --git a/post-processor/shell-local/post-processor.go b/post-processor/shell-local/post-processor.go index 818a8b44e..b1585a228 100644 --- a/post-processor/shell-local/post-processor.go +++ b/post-processor/shell-local/post-processor.go @@ -1,17 +1,8 @@ package shell_local import ( - "bufio" - "fmt" - "io/ioutil" - "log" - "os" - "sort" - "strings" - sl "github.com/hashicorp/packer/common/shell-local" "github.com/hashicorp/packer/packer" - "github.com/hashicorp/packer/template/interpolate" ) 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) { + // this particular post-processor doesn't do anything with the artifact + // except to return it. - scripts := make([]string, len(p.config.Scripts)) - copy(scripts, p.config.Scripts) - - // 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 - 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) + retBool, retErr := sl.Run(ui, &p.config) + if !retBool { + return nil, retBool, retErr } - // 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 + return artifact, retBool, retErr } diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index 615a7eb24..a56553245 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -1,8 +1,6 @@ package shell import ( - "fmt" - sl "github.com/hashicorp/packer/common/shell-local" "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 { - // Make another communicator for local - comm := &sl.Communicator{ - Ctx: p.config.Ctx, - 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) + _, retErr := sl.Run(ui, &p.config) + if retErr != nil { + return retErr } return nil