| package localexec |
| |
| import ( |
| "fmt" |
| "io/ioutil" |
| "os" |
| "strings" |
| "testing" |
| "time" |
| |
| "github.com/hashicorp/terraform/internal/provisioners" |
| "github.com/mitchellh/cli" |
| "github.com/zclconf/go-cty/cty" |
| ) |
| |
| func TestResourceProvider_Apply(t *testing.T) { |
| defer os.Remove("test_out") |
| 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) |
| } |
| |
| resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ |
| Config: c, |
| UIOutput: output, |
| }) |
| |
| if resp.Diagnostics.HasErrors() { |
| t.Fatalf("err: %v", resp.Diagnostics.Err()) |
| } |
| |
| // Check the file |
| raw, err := ioutil.ReadFile("test_out") |
| if err != nil { |
| t.Fatalf("err: %v", err) |
| } |
| |
| actual := strings.TrimSpace(string(raw)) |
| expected := "foo" |
| if actual != expected { |
| t.Fatalf("bad: %#v", actual) |
| } |
| } |
| |
| func TestResourceProvider_stop(t *testing.T) { |
| output := cli.NewMockUi() |
| p := New() |
| schema := p.GetSchema().Provisioner |
| |
| c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ |
| // bash/zsh/ksh will exec a single command in the same process. This |
| // makes certain there's a subprocess in the shell. |
| "command": cty.StringVal("sleep 30; sleep 30"), |
| })) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| doneCh := make(chan struct{}) |
| startTime := time.Now() |
| go func() { |
| defer close(doneCh) |
| // 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. |
| _ = p.ProvisionResource(provisioners.ProvisionResourceRequest{ |
| Config: c, |
| UIOutput: output, |
| }) |
| }() |
| |
| mustExceed := (50 * time.Millisecond) |
| select { |
| case <-doneCh: |
| 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) |
| } |
| |
| // Stop it |
| stopTime := time.Now() |
| p.Stop() |
| |
| maxTempl := "expected to finish under %s, finished in %s" |
| finishWithin := (2 * time.Second) |
| select { |
| case <-doneCh: |
| t.Logf(maxTempl, finishWithin, time.Since(stopTime)) |
| case <-time.After(finishWithin): |
| t.Fatalf(maxTempl, finishWithin, time.Since(stopTime)) |
| } |
| } |
| |
| func TestResourceProvider_ApplyCustomInterpreter(t *testing.T) { |
| output := cli.NewMockUi() |
| p := New() |
| |
| schema := p.GetSchema().Provisioner |
| |
| 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) |
| } |
| |
| resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ |
| Config: c, |
| UIOutput: output, |
| }) |
| |
| if resp.Diagnostics.HasErrors() { |
| t.Fatal(resp.Diagnostics.Err()) |
| } |
| |
| got := strings.TrimSpace(output.OutputWriter.String()) |
| want := `Executing: ["echo" "is" "not really an interpreter"] |
| is not really an interpreter` |
| if got != want { |
| t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) |
| } |
| } |
| |
| func TestResourceProvider_ApplyCustomWorkingDirectory(t *testing.T) { |
| testdir := "working_dir_test" |
| os.Mkdir(testdir, 0755) |
| defer os.Remove(testdir) |
| |
| 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) |
| } |
| |
| resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ |
| Config: c, |
| UIOutput: output, |
| }) |
| |
| if resp.Diagnostics.HasErrors() { |
| t.Fatal(resp.Diagnostics.Err()) |
| } |
| |
| dir, err := os.Getwd() |
| if err != nil { |
| t.Fatalf("err: %v", err) |
| } |
| |
| got := strings.TrimSpace(output.OutputWriter.String()) |
| want := "Executing: [\"/bin/sh\" \"-c\" \"echo `pwd`\"]\n" + dir + "/" + testdir |
| if got != want { |
| t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) |
| } |
| } |
| |
| func TestResourceProvider_ApplyCustomEnv(t *testing.T) { |
| 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) |
| } |
| |
| resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ |
| Config: c, |
| UIOutput: output, |
| }) |
| if resp.Diagnostics.HasErrors() { |
| t.Fatal(resp.Diagnostics.Err()) |
| } |
| |
| got := strings.TrimSpace(output.OutputWriter.String()) |
| want := `Executing: ["/bin/sh" "-c" "echo $FOO $BAR $BAZ"] |
| BAR 1 true` |
| if got != want { |
| t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) |
| } |
| } |
| |
| // Validate that Stop can Close can be called even when not provisioning. |
| func TestResourceProvisioner_StopClose(t *testing.T) { |
| p := New() |
| p.Stop() |
| p.Close() |
| } |
| |
| 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, |
| }) |
| }) |
| } |
| } |