Create a shell-local post processor
The following commits build on work from @vtolstov to create a post processor shell-local plugin. Please see his original work over at https://github.com/vtolstov/packer-post-processor-shell I have modified it slightly to output information onto the packer ui as shown in the below screenshot which executes a script that runs env. This plugin enables users to submit environmental variables to external external shell script(s) to do some post processing e.g. (Upload to somewhere, convert to different format, and so on) Most of the work is a merge from the provisioner shell and shell-local scripts. ![Example run of post processor shell-local](http://i.imgur.com/kJv6j9l.png) Signed-off-by: Ian Duffy <ian@ianduffy.ie>
This commit is contained in:
parent
28b045e546
commit
34b59bc051
|
@ -0,0 +1,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/post-processor/shell-local"
|
||||
"github.com/mitchellh/packer/packer/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server, err := plugin.Server()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.RegisterPostProcessor(new(shell_local.PostProcessor))
|
||||
server.Serve()
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package main
|
|
@ -0,0 +1 @@
|
|||
package main
|
|
@ -0,0 +1,59 @@
|
|||
package shell_local
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/mitchellh/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")
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package shell_local
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/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())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
package shell_local
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"github.com/mitchellh/packer/helper/config"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/template/interpolate"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
// An inline script to execute. Multiple strings are all executed
|
||||
// in the context of a single shell.
|
||||
Inline []string
|
||||
|
||||
// The shebang value used when running inline scripts.
|
||||
InlineShebang string `mapstructure:"inline_shebang"`
|
||||
|
||||
// The local path of the shell script to upload and execute.
|
||||
Script string
|
||||
|
||||
// An array of multiple scripts to run.
|
||||
Scripts []string
|
||||
|
||||
// An array of environment variables that will be injected before
|
||||
// your command(s) are executed.
|
||||
Vars []string `mapstructure:"environment_vars"`
|
||||
|
||||
// The command used to execute the script. The '{{ .Path }}' variable
|
||||
// should be used to specify where the script goes, {{ .Vars }}
|
||||
// can be used to inject the environment_vars into the environment.
|
||||
ExecuteCommand string `mapstructure:"execute_command"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
type PostProcessor struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
type ExecuteCommandTemplate struct {
|
||||
Vars string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p *PostProcessor) Configure(raws ...interface{}) error {
|
||||
err := config.Decode(&p.config, &config.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateContext: &p.config.ctx,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"execute_command",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.config.ExecuteCommand == "" {
|
||||
p.config.ExecuteCommand = "chmod +x {{.Path}}; {{.Vars}} {{.Path}}"
|
||||
}
|
||||
|
||||
if p.config.Inline != nil && len(p.config.Inline) == 0 {
|
||||
p.config.Inline = nil
|
||||
}
|
||||
|
||||
if p.config.InlineShebang == "" {
|
||||
p.config.InlineShebang = "/bin/sh -e"
|
||||
}
|
||||
|
||||
if p.config.Scripts == nil {
|
||||
p.config.Scripts = make([]string, 0)
|
||||
}
|
||||
|
||||
if p.config.Vars == nil {
|
||||
p.config.Vars = make([]string, 0)
|
||||
}
|
||||
|
||||
var errs *packer.MultiError
|
||||
if p.config.Script != "" && len(p.config.Scripts) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Only one of script or scripts can be specified."))
|
||||
}
|
||||
|
||||
if p.config.Script != "" {
|
||||
p.config.Scripts = []string{p.config.Script}
|
||||
}
|
||||
|
||||
if len(p.config.Scripts) == 0 && p.config.Inline == nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Either a script file or inline script must be specified."))
|
||||
} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
errors.New("Only a script file or an inline script can be specified, not both."))
|
||||
}
|
||||
|
||||
for _, path := range p.config.Scripts {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Bad script '%s': %s", path, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Do a check for bad environment variables, such as '=foo', 'foobar'
|
||||
for idx, kv := range p.config.Vars {
|
||||
vs := strings.SplitN(kv, "=", 2)
|
||||
if len(vs) != 2 || vs[0] == "" {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Environment variable not in format 'key=value': %s", kv))
|
||||
} else {
|
||||
// Replace single quotes so they parse
|
||||
vs[1] = strings.Replace(vs[1], "'", `'"'"'`, -1)
|
||||
|
||||
// Single quote env var values
|
||||
p.config.Vars[idx] = fmt.Sprintf("%s='%s'", vs[0], vs[1])
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PostProcessor) PostProcess(ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
// Build our variables up by adding in the build name and builder type
|
||||
envVars := make([]string, len(p.config.Vars)+2)
|
||||
envVars[0] = fmt.Sprintf("PACKER_BUILD_NAME='%s'", p.config.PackerBuildName)
|
||||
envVars[1] = fmt.Sprintf("PACKER_BUILDER_TYPE='%s'", p.config.PackerBuilderType)
|
||||
copy(envVars[2:], p.config.Vars)
|
||||
|
||||
for _, path := range scripts {
|
||||
ui.Say(fmt.Sprintf("Post processing with local shell script: %s", path))
|
||||
|
||||
// Flatten the environment variables
|
||||
flattendVars := strings.Join(envVars, " ")
|
||||
|
||||
p.config.ctx.Data = &ExecuteCommandTemplate{
|
||||
Vars: flattendVars,
|
||||
Path: path,
|
||||
}
|
||||
|
||||
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("Error processing command: %s", err)
|
||||
}
|
||||
|
||||
comm := &Communicator{}
|
||||
|
||||
cmd := &packer.RemoteCmd{Command: command}
|
||||
|
||||
ui.Say(fmt.Sprintf(
|
||||
"Executing local script: %s",
|
||||
path))
|
||||
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.",
|
||||
path)
|
||||
}
|
||||
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,
|
||||
path)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil, true, nil
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
package shell_local
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPostProcessor_ImplementsPostProcessor(t *testing.T) {
|
||||
var _ packer.PostProcessor = new(PostProcessor)
|
||||
}
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"inline": []interface{}{"foo", "bar"},
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessor_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &PostProcessor{}
|
||||
if _, ok := raw.(packer.PostProcessor); !ok {
|
||||
t.Fatalf("must be a post processor")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_Defaults(t *testing.T) {
|
||||
var p PostProcessor
|
||||
config := testConfig()
|
||||
|
||||
err := p.Configure(config)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_InlineShebang(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
delete(config, "inline_shebang")
|
||||
p := new(PostProcessor)
|
||||
err := p.Configure(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if p.config.InlineShebang != "/bin/sh -e" {
|
||||
t.Fatalf("bad value: %s", p.config.InlineShebang)
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
config["inline_shebang"] = "foo"
|
||||
p = new(PostProcessor)
|
||||
err = p.Configure(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if p.config.InlineShebang != "foo" {
|
||||
t.Fatalf("bad value: %s", p.config.InlineShebang)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_InvalidKey(t *testing.T) {
|
||||
var p PostProcessor
|
||||
config := testConfig()
|
||||
|
||||
// Add a random key
|
||||
config["i_should_not_be_valid"] = true
|
||||
err := p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_Script(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
config["script"] = "/this/should/not/exist"
|
||||
p := new(PostProcessor)
|
||||
err := p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["script"] = tf.Name()
|
||||
p = new(PostProcessor)
|
||||
err = p.Configure(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_ScriptAndInline(t *testing.T) {
|
||||
var p PostProcessor
|
||||
config := testConfig()
|
||||
|
||||
delete(config, "inline")
|
||||
delete(config, "script")
|
||||
err := p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with both
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["inline"] = []interface{}{"foo"}
|
||||
config["script"] = tf.Name()
|
||||
err = p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_ScriptAndScripts(t *testing.T) {
|
||||
var p PostProcessor
|
||||
config := testConfig()
|
||||
|
||||
// Test with both
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["inline"] = []interface{}{"foo"}
|
||||
config["scripts"] = []string{tf.Name()}
|
||||
err = p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_Scripts(t *testing.T) {
|
||||
config := testConfig()
|
||||
delete(config, "inline")
|
||||
|
||||
config["scripts"] = []string{}
|
||||
p := new(PostProcessor)
|
||||
err := p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good one
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("error tempfile: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config["scripts"] = []string{tf.Name()}
|
||||
p = new(PostProcessor)
|
||||
err = p.Configure(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorPrepare_EnvironmentVars(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
// Test with a bad case
|
||||
config["environment_vars"] = []string{"badvar", "good=var"}
|
||||
p := new(PostProcessor)
|
||||
err := p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a trickier case
|
||||
config["environment_vars"] = []string{"=bad"}
|
||||
p = new(PostProcessor)
|
||||
err = p.Configure(config)
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test with a good case
|
||||
// Note: baz= is a real env variable, just empty
|
||||
config["environment_vars"] = []string{"FOO=bar", "baz="}
|
||||
p = new(PostProcessor)
|
||||
err = p.Configure(config)
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostProcessorQuote_EnvironmentVars(t *testing.T) {
|
||||
config := testConfig()
|
||||
|
||||
config["environment_vars"] = []string{"keyone=valueone", "keytwo=value\ntwo"}
|
||||
p := new(PostProcessor)
|
||||
p.Configure(config)
|
||||
|
||||
expectedValue := "keyone='valueone'"
|
||||
if p.config.Vars[0] != expectedValue {
|
||||
t.Fatalf("%s should be equal to %s", p.config.Vars[0], expectedValue)
|
||||
}
|
||||
|
||||
expectedValue = "keytwo='value\ntwo'"
|
||||
if p.config.Vars[1] != expectedValue {
|
||||
t.Fatalf("%s should be equal to %s", p.config.Vars[1], expectedValue)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
description: |
|
||||
The shell-local Packer post processor enables users to do some post processing after artifacts have been built.
|
||||
layout: docs
|
||||
page_title: Local Shell Post Processor
|
||||
...
|
||||
|
||||
# Local Shell Post Processor
|
||||
|
||||
Type: `shell-local`
|
||||
|
||||
The local shell post processor executes scripts locally during the post processing stage. Shell local provides an easy
|
||||
way to automate executing some task with the packer outputs.
|
||||
|
||||
## Basic example
|
||||
|
||||
The example below is fully functional.
|
||||
|
||||
``` {.javascript}
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": ["echo foo"]
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
The reference of available configuration options is listed below. The only
|
||||
required element is either "inline" or "script". Every other option is optional.
|
||||
|
||||
Exactly *one* of the following is required:
|
||||
|
||||
- `inline` (array of strings) - This is an array of commands to execute. The
|
||||
commands are concatenated by newlines and turned into a single file, so they
|
||||
are all executed within the same context. This allows you to change
|
||||
directories in one command and use something in the directory in the next
|
||||
and so on. Inline scripts are the easiest way to pull off simple tasks
|
||||
within the machine.
|
||||
|
||||
- `script` (string) - The path to a script to upload and execute in
|
||||
the machine. This path can be absolute or relative. If it is relative, it is
|
||||
relative to the working directory when Packer is executed.
|
||||
|
||||
- `scripts` (array of strings) - An array of scripts to execute. The scripts
|
||||
will be uploaded and executed in the order specified. Each script is
|
||||
executed in isolation, so state such as variables from one script won't
|
||||
carry on to the next.
|
||||
|
||||
Optional parameters:
|
||||
|
||||
- `binary` (boolean) - If true, specifies that the script(s) are binary files,
|
||||
and Packer should therefore not convert Windows line endings to Unix line
|
||||
endings (if there are any). By default this is false.
|
||||
|
||||
- `environment_vars` (array of strings) - An array of key/value pairs to
|
||||
inject prior to the execute\_command. The format should be `key=value`.
|
||||
Packer injects some environmental variables by default into the environment,
|
||||
as well, which are covered in the section below.
|
||||
|
||||
- `execute_command` (string) - The command to use to execute the script. By
|
||||
default this is `chmod +x {{ .Path }}; {{ .Vars }} {{ .Path }}`. The value
|
||||
of this is treated as [configuration
|
||||
template](/docs/templates/configuration-templates.html). There are two
|
||||
available variables: `Path`, which is the path to the script to run, and
|
||||
`Vars`, which is the list of `environment_vars`, if configured.
|
||||
|
||||
- `inline_shebang` (string) - The
|
||||
[shebang](http://en.wikipedia.org/wiki/Shebang_%28Unix%29) value to use when
|
||||
running commands specified by `inline`. By default, this is `/bin/sh -e`. If
|
||||
you're not using `inline`, then this configuration has no effect.
|
||||
**Important:** If you customize this, be sure to include something like the
|
||||
`-e` flag, otherwise individual steps failing won't fail the provisioner.
|
||||
|
||||
## Execute Command Example
|
||||
|
||||
To many new users, the `execute_command` is puzzling. However, it provides an
|
||||
important function: customization of how the command is executed. The most
|
||||
common use case for this is dealing with **sudo password prompts**. You may also
|
||||
need to customize this if you use a non-POSIX shell, such as `tcsh` on FreeBSD.
|
||||
|
||||
## Default Environmental Variables
|
||||
|
||||
In addition to being able to specify custom environmental variables using the
|
||||
`environment_vars` configuration, the provisioner automatically defines certain
|
||||
commonly useful environmental variables:
|
||||
|
||||
- `PACKER_BUILD_NAME` is set to the name of the build that Packer is running.
|
||||
This is most useful when Packer is making multiple builds and you want to
|
||||
distinguish them slightly from a common provisioning script.
|
||||
|
||||
- `PACKER_BUILDER_TYPE` is the type of the builder that was used to create the
|
||||
machine that the script is running on. This is useful if you want to run
|
||||
only certain parts of the script on systems built with certain builders.
|
Loading…
Reference in New Issue