diff --git a/common/shell-local/config.go b/common/shell-local/config.go index 8c73d0f57..80751aee7 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "runtime" "strings" @@ -44,6 +45,8 @@ type Config struct { // can be used to inject the environment_vars into the environment. ExecuteCommand []string `mapstructure:"execute_command"` + UseLinuxPathing bool `mapstructure:"use_linux_pathing"` + Ctx interpolate.Context } @@ -67,11 +70,6 @@ func Decode(config *Config, raws ...interface{}) error { func Validate(config *Config) error { var errs *packer.MultiError - // Do not treat these defaults as a source of truth; the shell-local - // provisioner sets these defaults before Validate is called. Eventually - // we will have to bring the provisioner and post-processor defaults in - // line with one another, but for now the following may or may not be - // applied depending on where Validate is being called from. if runtime.GOOS == "windows" { if len(config.ExecuteCommand) == 0 { config.ExecuteCommand = []string{ @@ -89,7 +87,8 @@ func Validate(config *Config) error { config.ExecuteCommand = []string{ "/bin/sh", "-c", - "{{.Vars}} {{.Script}}", + "{{.Vars}}", + "{{.Script}}", } } } @@ -146,6 +145,15 @@ func Validate(config *Config) error { fmt.Errorf("Bad script '%s': %s", path, err)) } } + if config.UseLinuxPathing { + for index, script := range config.Scripts { + converted, err := convertToLinuxPath(script) + if err != nil { + return err + } + config.Scripts[index] = converted + } + } // Do a check for bad environment variables, such as '=foo', 'foobar' for _, kv := range config.Vars { @@ -162,3 +170,16 @@ func Validate(config *Config) error { return nil } + +// C:/path/to/your/file becomes /mnt/c/path/to/your/file +func convertToLinuxPath(winPath string) (string, error) { + // get absolute path of script, and morph it into the bash path + winAbsPath, err := filepath.Abs(winPath) + if err != nil { + return "", fmt.Errorf("Error converting %s to absolute path: %s", winPath, err.Error()) + } + winAbsPath = strings.Replace(winAbsPath, "\\", "/", -1) + splitPath := strings.SplitN(winAbsPath, ":/", 2) + winBashPath := fmt.Sprintf("/mnt/%s/%s", strings.ToLower(splitPath[0]), splitPath[1]) + return winBashPath, nil +} diff --git a/common/shell-local/run.go b/common/shell-local/run.go index 5545d56d7..ea8043737 100644 --- a/common/shell-local/run.go +++ b/common/shell-local/run.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "log" "os" + "runtime" "sort" "strings" @@ -144,8 +145,17 @@ func createFlattenedEnvVars(config *Config) (flattened string) { sort.Strings(keys) // Re-assemble vars surrounding value with single quotes and flatten + if runtime.GOOS == "windows" { + log.Printf("MEGAN NEED TO IMPLEMENT") + // createEnvVarsSourceFileWindows() + } for _, key := range keys { flattened += fmt.Sprintf("%s='%s' ", key, envVars[key]) } return } + +// func createFlattenedEnvVarsWindows( +// // The default shell, cmd, can set vars via dot sourcing +// // set TESTXYZ=XYZ +// ) diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index a58f0c859..16c3806e4 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -1,11 +1,6 @@ package shell import ( - "fmt" - "path/filepath" - "runtime" - "strings" - sl "github.com/hashicorp/packer/common/shell-local" "github.com/hashicorp/packer/packer" ) @@ -19,44 +14,15 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { if err != nil { return err } - convertPath := false - if len(p.config.ExecuteCommand) == 0 && runtime.GOOS == "windows" { - convertPath = true - p.config.ExecuteCommand = []string{ - "bash", - "-c", - "{{.Vars}} {{.Script}}", - } - } err = sl.Validate(&p.config) if err != nil { return err } - if convertPath { - for index, script := range p.config.Scripts { - p.config.Scripts[index], err = convertToWindowsBashPath(script) - if err != nil { - return err - } - } - } - return nil } -func convertToWindowsBashPath(winPath string) (string, error) { - // get absolute path of script, and morph it into the bash path - winAbsPath, err := filepath.Abs(winPath) - if err != nil { - return "", fmt.Errorf("Error converting %s to absolute path: %s", winPath, err.Error()) - } - winAbsPath = strings.Replace(winAbsPath, "\\", "/", -1) - winBashPath := strings.Replace(winAbsPath, "C:/", "/mnt/c/", 1) - return winBashPath, nil -} - func (p *Provisioner) Provision(ui packer.Ui, _ packer.Communicator) error { _, retErr := sl.Run(ui, &p.config) if retErr != nil { diff --git a/website/source/docs/post-processors/shell-local.html.md b/website/source/docs/post-processors/shell-local.html.md index 26ef7871f..e2bc324da 100644 --- a/website/source/docs/post-processors/shell-local.html.md +++ b/website/source/docs/post-processors/shell-local.html.md @@ -60,7 +60,7 @@ Optional parameters: as well, which are covered in the section below. - `execute_command` (array of strings) - The command used to execute the script. By - default this is `["sh", "-c", "chmod +x \"{{.Script}}\"; {{.Vars}} \"{{.Script}}\""]` + default this is `["/bin/sh", "-c", "{{.Vars}}, "{{.Script}}"]` on unix and `["cmd", "/c", "{{.Vars}}", "{{.Script}}"]` on windows. This is treated as a [template engine](/docs/templates/engine.html). There are two available variables: `Script`, which is the path to the script @@ -69,7 +69,9 @@ Optional parameters: array is the shell program you want to use (for example, "sh" or "/usr/local/bin/zsh" or even "powershell.exe" although anything other than a flavor of the shell command language is not explicitly supported and may - be broken by assumptions made within Packer). + be broken by assumptions made within Packer). It's worth noting that if you + choose to try to use shell-local for Powershell or other Windows commands, + the environment variables will not be set properly for your environment. For backwards compatibility, `execute_command` will accept a string insetad of an array of strings. If a single string or an array of strings with only @@ -89,13 +91,46 @@ Optional parameters: **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 +- `use_linux_pathing` (bool) - This is only relevant to windows hosts. If you + are running Packer in a Windows environment with the Windows Subsystem for + Linux feature enabled, and would like to invoke a bash script rather than + invoking a Cmd script, you'll need to set this flag to true; it tells Packer + to use the linux subsystem path for your script rather than the Windows path. + (e.g. /mnt/c/path/to/your/file instead of C:/path/to/your/file). + +## Execute Command 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. +### The Windows Linux Subsystem + +If you have a bash script that you'd like to run on your Windows Linux +Subsystem as part of the shell-local post-processor, you must set +`execute_command` and `use_linux_pathing`. + +The example below is a fully functional test config. + +``` +{ + "builders": [ + { + "type": "null", + "communicator": "none" + } + ], + "provisioners": [ + { + "type": "shell-local", + "environment_vars": ["PROVISIONERTEST=ProvisionerTest1"], + "execute_command": ["bash", "-c", "{{.Vars}} {{.Script}}"] + "use_linux_pathing": true + "scripts": ["./scripts/.sh"] + }, +``` + ## Default Environmental Variables In addition to being able to specify custom environmental variables using the