provisioner/local-exec: allow user to specify interpreter
This commit is contained in:
parent
1dc3611fbd
commit
5d5f8224d2
|
@ -28,6 +28,12 @@ func Provisioner() terraform.ResourceProvisioner {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"interpreter": &schema.Schema{
|
||||||
|
Type: schema.TypeList,
|
||||||
|
Elem: &schema.Schema{Type: schema.TypeString},
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
ApplyFunc: applyFn,
|
ApplyFunc: applyFn,
|
||||||
|
@ -39,19 +45,29 @@ func applyFn(ctx context.Context) error {
|
||||||
o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput)
|
o := ctx.Value(schema.ProvOutputKey).(terraform.UIOutput)
|
||||||
|
|
||||||
command := data.Get("command").(string)
|
command := data.Get("command").(string)
|
||||||
|
|
||||||
if command == "" {
|
if command == "" {
|
||||||
return fmt.Errorf("local-exec provisioner command must be a non-empty string")
|
return fmt.Errorf("local-exec provisioner command must be a non-empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the command using a shell
|
// Execute the command using a shell
|
||||||
var shell, flag string
|
interpreter := data.Get("interpreter").([]interface{})
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
shell = "cmd"
|
var cmdargs []string
|
||||||
flag = "/C"
|
if len(interpreter) > 0 {
|
||||||
} else {
|
for _, i := range interpreter {
|
||||||
shell = "/bin/sh"
|
if arg, ok := i.(string); ok {
|
||||||
flag = "-c"
|
cmdargs = append(cmdargs, arg)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
cmdargs = []string{"cmd", "/C"}
|
||||||
|
} else {
|
||||||
|
cmdargs = []string{"/bin/sh", "-c"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmdargs = append(cmdargs, command)
|
||||||
|
|
||||||
// Setup the reader that will read the output from the command.
|
// Setup the reader that will read the output from the command.
|
||||||
// We use an os.Pipe so that the *os.File can be passed directly to the
|
// We use an os.Pipe so that the *os.File can be passed directly to the
|
||||||
|
@ -63,7 +79,7 @@ func applyFn(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the command
|
// Setup the command
|
||||||
cmd := exec.CommandContext(ctx, shell, flag, command)
|
cmd := exec.CommandContext(ctx, cmdargs[0], cmdargs[1:]...)
|
||||||
cmd.Stderr = pw
|
cmd.Stderr = pw
|
||||||
cmd.Stdout = pw
|
cmd.Stdout = pw
|
||||||
|
|
||||||
|
@ -77,9 +93,7 @@ func applyFn(ctx context.Context) error {
|
||||||
go copyOutput(o, tee, copyDoneCh)
|
go copyOutput(o, tee, copyDoneCh)
|
||||||
|
|
||||||
// Output what we're about to run
|
// Output what we're about to run
|
||||||
o.Output(fmt.Sprintf(
|
o.Output(fmt.Sprintf("Executing: %q", cmdargs))
|
||||||
"Executing: %s %s \"%s\"",
|
|
||||||
shell, flag, command))
|
|
||||||
|
|
||||||
// Start the command
|
// Start the command
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
|
|
|
@ -125,3 +125,23 @@ func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfi
|
||||||
|
|
||||||
return terraform.NewResourceConfig(r)
|
return terraform.NewResourceConfig(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourceProvider_ApplyCustomInterpreter(t *testing.T) {
|
||||||
|
c := testConfig(t, map[string]interface{}{
|
||||||
|
"interpreter": []interface{}{"echo", "is"},
|
||||||
|
"command": "not really an interpreter",
|
||||||
|
})
|
||||||
|
|
||||||
|
output := new(terraform.MockUIOutput)
|
||||||
|
p := Provisioner()
|
||||||
|
|
||||||
|
if err := p.Apply(output, nil, c); err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
got := strings.TrimSpace(output.OutputMessage)
|
||||||
|
want := "is not really an interpreter"
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("wrong output\ngot: %s\nwant: %s", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,3 +38,31 @@ The following arguments are supported:
|
||||||
as a relative path to the current working directory or as an absolute path.
|
as a relative path to the current working directory or as an absolute path.
|
||||||
It is evaluated in a shell, and can use environment variables or Terraform
|
It is evaluated in a shell, and can use environment variables or Terraform
|
||||||
variables.
|
variables.
|
||||||
|
|
||||||
|
* `interpreter` - (Optional) If provided, this is a list of interpreter
|
||||||
|
arguments used to execute the command. The first argument is the
|
||||||
|
interpreter itself. It can be provided as a relative path to the current
|
||||||
|
working directory or as an absolute path. The remaining arguments are
|
||||||
|
appended prior to the command. This allows building command lines of the
|
||||||
|
form "/bin/bash", "-c", "echo foo". If `interpreter` is unspecified,
|
||||||
|
sensible defaults will be chosen based on the system OS.
|
||||||
|
|
||||||
|
### Interpreter Examples
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "null_resource" "example1" {
|
||||||
|
provisioner "local-exec" {
|
||||||
|
command = "open WFH, '>completed.txt' and print WFH scalar localtime"
|
||||||
|
interpreter = ["perl", "-e"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
resource "null_resource" "example2" {
|
||||||
|
provisioner "local-exec" {
|
||||||
|
command = "Get-Date > completed.txt"
|
||||||
|
interpreter = ["PowerShell", "-Command"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
Loading…
Reference in New Issue