add new feature for telling shell-local whether to use linux pathing on windows; update docs with some examples.

This commit is contained in:
Megan Marsh 2018-03-08 16:42:17 -08:00
parent e983a94a88
commit 51bcc7aa13
4 changed files with 75 additions and 43 deletions

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path/filepath"
"runtime" "runtime"
"strings" "strings"
@ -44,6 +45,8 @@ type Config struct {
// can be used to inject the environment_vars into the environment. // can be used to inject the environment_vars into the environment.
ExecuteCommand []string `mapstructure:"execute_command"` ExecuteCommand []string `mapstructure:"execute_command"`
UseLinuxPathing bool `mapstructure:"use_linux_pathing"`
Ctx interpolate.Context Ctx interpolate.Context
} }
@ -67,11 +70,6 @@ func Decode(config *Config, raws ...interface{}) error {
func Validate(config *Config) error { func Validate(config *Config) error {
var errs *packer.MultiError 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 runtime.GOOS == "windows" {
if len(config.ExecuteCommand) == 0 { if len(config.ExecuteCommand) == 0 {
config.ExecuteCommand = []string{ config.ExecuteCommand = []string{
@ -89,7 +87,8 @@ func Validate(config *Config) error {
config.ExecuteCommand = []string{ config.ExecuteCommand = []string{
"/bin/sh", "/bin/sh",
"-c", "-c",
"{{.Vars}} {{.Script}}", "{{.Vars}}",
"{{.Script}}",
} }
} }
} }
@ -146,6 +145,15 @@ func Validate(config *Config) error {
fmt.Errorf("Bad script '%s': %s", path, err)) 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' // Do a check for bad environment variables, such as '=foo', 'foobar'
for _, kv := range config.Vars { for _, kv := range config.Vars {
@ -162,3 +170,16 @@ func Validate(config *Config) error {
return nil 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
}

View File

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"runtime"
"sort" "sort"
"strings" "strings"
@ -144,8 +145,17 @@ func createFlattenedEnvVars(config *Config) (flattened string) {
sort.Strings(keys) sort.Strings(keys)
// Re-assemble vars surrounding value with single quotes and flatten // 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 { for _, key := range keys {
flattened += fmt.Sprintf("%s='%s' ", key, envVars[key]) flattened += fmt.Sprintf("%s='%s' ", key, envVars[key])
} }
return return
} }
// func createFlattenedEnvVarsWindows(
// // The default shell, cmd, can set vars via dot sourcing
// // set TESTXYZ=XYZ
// )

View File

@ -1,11 +1,6 @@
package shell package shell
import ( import (
"fmt"
"path/filepath"
"runtime"
"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"
) )
@ -19,44 +14,15 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
if err != nil { if err != nil {
return err 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) err = sl.Validate(&p.config)
if err != nil { if err != nil {
return err 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 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 { func (p *Provisioner) Provision(ui packer.Ui, _ packer.Communicator) error {
_, retErr := sl.Run(ui, &p.config) _, retErr := sl.Run(ui, &p.config)
if retErr != nil { if retErr != nil {

View File

@ -60,7 +60,7 @@ Optional parameters:
as well, which are covered in the section below. as well, which are covered in the section below.
- `execute_command` (array of strings) - The command used to execute the script. By - `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. on unix and `["cmd", "/c", "{{.Vars}}", "{{.Script}}"]` on windows.
This is treated as a [template engine](/docs/templates/engine.html). This is treated as a [template engine](/docs/templates/engine.html).
There are two available variables: `Script`, which is the path to the script 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 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 "/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 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 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 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 **Important:** If you customize this, be sure to include something like the
`-e` flag, otherwise individual steps failing won't fail the provisioner. `-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 To many new users, the `execute_command` is puzzling. However, it provides an
important function: customization of how the command is executed. The most 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 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. 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 ## Default Environmental Variables
In addition to being able to specify custom environmental variables using the In addition to being able to specify custom environmental variables using the