2014-07-09 22:34:08 +02:00
|
|
|
package localexec
|
|
|
|
|
|
|
|
import (
|
2021-04-20 18:31:32 +02:00
|
|
|
"fmt"
|
2014-07-10 01:36:46 +02:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2014-07-10 22:09:09 +02:00
|
|
|
"strings"
|
2014-07-09 22:34:08 +02:00
|
|
|
"testing"
|
2016-12-23 01:06:40 +01:00
|
|
|
"time"
|
2014-07-09 22:34:08 +02:00
|
|
|
|
2021-05-17 19:51:48 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/provisioners"
|
2020-11-25 15:24:10 +01:00
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
2014-07-09 22:34:08 +02:00
|
|
|
)
|
|
|
|
|
2014-07-10 01:36:46 +02:00
|
|
|
func TestResourceProvider_Apply(t *testing.T) {
|
|
|
|
defer os.Remove("test_out")
|
2020-11-25 15:24:10 +01:00
|
|
|
output := cli.NewMockUi()
|
|
|
|
p := New()
|
|
|
|
schema := p.GetSchema().Provisioner
|
|
|
|
c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo foo > test_out"),
|
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-07-10 01:36:46 +02:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
|
|
Config: c,
|
|
|
|
UIOutput: output,
|
|
|
|
})
|
2017-05-19 20:42:14 +02:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
if resp.Diagnostics.HasErrors() {
|
|
|
|
t.Fatalf("err: %v", resp.Diagnostics.Err())
|
2014-07-10 01:36:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check the file
|
|
|
|
raw, err := ioutil.ReadFile("test_out")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-07-10 22:09:09 +02:00
|
|
|
actual := strings.TrimSpace(string(raw))
|
|
|
|
expected := "foo"
|
|
|
|
if actual != expected {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
2014-07-10 01:36:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-23 01:06:40 +01:00
|
|
|
func TestResourceProvider_stop(t *testing.T) {
|
2020-11-25 15:24:10 +01:00
|
|
|
output := cli.NewMockUi()
|
|
|
|
p := New()
|
|
|
|
schema := p.GetSchema().Provisioner
|
|
|
|
|
|
|
|
c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
2017-02-01 00:42:56 +01:00
|
|
|
// bash/zsh/ksh will exec a single command in the same process. This
|
|
|
|
// makes certain there's a subprocess in the shell.
|
2020-11-25 15:24:10 +01:00
|
|
|
"command": cty.StringVal("sleep 30; sleep 30"),
|
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-12-23 01:06:40 +01:00
|
|
|
|
|
|
|
doneCh := make(chan struct{})
|
2017-08-22 04:05:49 +02:00
|
|
|
startTime := time.Now()
|
2016-12-23 01:06:40 +01:00
|
|
|
go func() {
|
|
|
|
defer close(doneCh)
|
2017-08-22 04:05:49 +02:00
|
|
|
// The functionality of p.Apply is tested in TestResourceProvider_Apply.
|
|
|
|
// Because p.Apply is called in a goroutine, trying to t.Fatal() on its
|
|
|
|
// result would be ignored or would cause a panic if the parent goroutine
|
|
|
|
// has already completed.
|
2020-11-25 15:24:10 +01:00
|
|
|
_ = p.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
|
|
Config: c,
|
|
|
|
UIOutput: output,
|
|
|
|
})
|
2016-12-23 01:06:40 +01:00
|
|
|
}()
|
|
|
|
|
2017-08-22 04:05:49 +02:00
|
|
|
mustExceed := (50 * time.Millisecond)
|
2016-12-23 01:06:40 +01:00
|
|
|
select {
|
|
|
|
case <-doneCh:
|
2017-08-22 04:05:49 +02:00
|
|
|
t.Fatalf("expected to finish sometime after %s finished in %s", mustExceed, time.Since(startTime))
|
|
|
|
case <-time.After(mustExceed):
|
|
|
|
t.Logf("correctly took longer than %s", mustExceed)
|
2016-12-23 01:06:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Stop it
|
2017-08-22 04:05:49 +02:00
|
|
|
stopTime := time.Now()
|
2016-12-23 01:06:40 +01:00
|
|
|
p.Stop()
|
|
|
|
|
2017-08-22 04:05:49 +02:00
|
|
|
maxTempl := "expected to finish under %s, finished in %s"
|
|
|
|
finishWithin := (2 * time.Second)
|
2016-12-23 01:06:40 +01:00
|
|
|
select {
|
|
|
|
case <-doneCh:
|
2017-08-22 04:05:49 +02:00
|
|
|
t.Logf(maxTempl, finishWithin, time.Since(stopTime))
|
|
|
|
case <-time.After(finishWithin):
|
|
|
|
t.Fatalf(maxTempl, finishWithin, time.Since(stopTime))
|
2016-12-23 01:06:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
func TestResourceProvider_ApplyCustomInterpreter(t *testing.T) {
|
|
|
|
output := cli.NewMockUi()
|
|
|
|
p := New()
|
2014-07-10 01:36:46 +02:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
schema := p.GetSchema().Provisioner
|
2017-05-19 20:42:14 +02:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"interpreter": cty.ListVal([]cty.Value{cty.StringVal("echo"), cty.StringVal("is")}),
|
|
|
|
"command": cty.StringVal("not really an interpreter"),
|
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
2014-07-10 01:36:46 +02:00
|
|
|
}
|
2017-08-22 19:07:32 +02:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
|
|
Config: c,
|
|
|
|
UIOutput: output,
|
2017-08-22 19:07:32 +02:00
|
|
|
})
|
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
if resp.Diagnostics.HasErrors() {
|
|
|
|
t.Fatal(resp.Diagnostics.Err())
|
2017-08-22 19:07:32 +02:00
|
|
|
}
|
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
got := strings.TrimSpace(output.OutputWriter.String())
|
|
|
|
want := `Executing: ["echo" "is" "not really an interpreter"]
|
|
|
|
is not really an interpreter`
|
2017-08-22 19:07:32 +02:00
|
|
|
if got != want {
|
|
|
|
t.Errorf("wrong output\ngot: %s\nwant: %s", got, want)
|
|
|
|
}
|
|
|
|
}
|
2018-02-16 20:31:11 +01:00
|
|
|
|
|
|
|
func TestResourceProvider_ApplyCustomWorkingDirectory(t *testing.T) {
|
|
|
|
testdir := "working_dir_test"
|
|
|
|
os.Mkdir(testdir, 0755)
|
|
|
|
defer os.Remove(testdir)
|
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
output := cli.NewMockUi()
|
|
|
|
p := New()
|
|
|
|
schema := p.GetSchema().Provisioner
|
|
|
|
|
|
|
|
c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"working_dir": cty.StringVal(testdir),
|
|
|
|
"command": cty.StringVal("echo `pwd`"),
|
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2018-02-16 20:31:11 +01:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
|
|
Config: c,
|
|
|
|
UIOutput: output,
|
|
|
|
})
|
2018-02-16 20:31:11 +01:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
if resp.Diagnostics.HasErrors() {
|
|
|
|
t.Fatal(resp.Diagnostics.Err())
|
2018-02-16 20:31:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dir, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
got := strings.TrimSpace(output.OutputWriter.String())
|
|
|
|
want := "Executing: [\"/bin/sh\" \"-c\" \"echo `pwd`\"]\n" + dir + "/" + testdir
|
2018-02-16 20:31:11 +01:00
|
|
|
if got != want {
|
|
|
|
t.Errorf("wrong output\ngot: %s\nwant: %s", got, want)
|
|
|
|
}
|
|
|
|
}
|
2018-03-06 00:58:49 +01:00
|
|
|
|
|
|
|
func TestResourceProvider_ApplyCustomEnv(t *testing.T) {
|
2020-11-25 15:24:10 +01:00
|
|
|
output := cli.NewMockUi()
|
|
|
|
p := New()
|
|
|
|
schema := p.GetSchema().Provisioner
|
|
|
|
|
|
|
|
c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo $FOO $BAR $BAZ"),
|
|
|
|
"environment": cty.MapVal(map[string]cty.Value{
|
|
|
|
"FOO": cty.StringVal("BAR"),
|
|
|
|
"BAR": cty.StringVal("1"),
|
|
|
|
"BAZ": cty.StringVal("true"),
|
|
|
|
}),
|
|
|
|
}))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2018-03-06 00:58:49 +01:00
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
|
|
Config: c,
|
|
|
|
UIOutput: output,
|
|
|
|
})
|
|
|
|
if resp.Diagnostics.HasErrors() {
|
|
|
|
t.Fatal(resp.Diagnostics.Err())
|
2018-03-06 00:58:49 +01:00
|
|
|
}
|
|
|
|
|
2020-11-25 15:24:10 +01:00
|
|
|
got := strings.TrimSpace(output.OutputWriter.String())
|
|
|
|
want := `Executing: ["/bin/sh" "-c" "echo $FOO $BAR $BAZ"]
|
|
|
|
BAR 1 true`
|
2018-03-06 00:58:49 +01:00
|
|
|
if got != want {
|
|
|
|
t.Errorf("wrong output\ngot: %s\nwant: %s", got, want)
|
|
|
|
}
|
|
|
|
}
|
2021-01-08 18:36:45 +01:00
|
|
|
|
|
|
|
// Validate that Stop can Close can be called even when not provisioning.
|
|
|
|
func TestResourceProvisioner_StopClose(t *testing.T) {
|
|
|
|
p := New()
|
|
|
|
p.Stop()
|
|
|
|
p.Close()
|
|
|
|
}
|
2021-04-20 18:31:32 +02:00
|
|
|
|
|
|
|
func TestResourceProvisioner_nullsInOptionals(t *testing.T) {
|
|
|
|
output := cli.NewMockUi()
|
|
|
|
p := New()
|
|
|
|
schema := p.GetSchema().Provisioner
|
|
|
|
|
|
|
|
for i, cfg := range []cty.Value{
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo OK"),
|
|
|
|
"environment": cty.MapVal(map[string]cty.Value{
|
|
|
|
"FOO": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo OK"),
|
|
|
|
"environment": cty.NullVal(cty.Map(cty.String)),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo OK"),
|
|
|
|
"interpreter": cty.ListVal([]cty.Value{cty.NullVal(cty.String)}),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo OK"),
|
|
|
|
"interpreter": cty.NullVal(cty.List(cty.String)),
|
|
|
|
}),
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"command": cty.StringVal("echo OK"),
|
|
|
|
"working_dir": cty.NullVal(cty.String),
|
|
|
|
}),
|
|
|
|
} {
|
|
|
|
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
|
|
|
|
|
|
|
cfg, err := schema.CoerceValue(cfg)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// verifying there are no panics
|
|
|
|
p.ProvisionResource(provisioners.ProvisionResourceRequest{
|
|
|
|
Config: cfg,
|
|
|
|
UIOutput: output,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|