From 0fb87cd96ba17628ebc17d17b89083d285007bae Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 22 Dec 2016 16:06:40 -0800 Subject: [PATCH] provisioners/local-exec: stoppable This modifies local-exec to be stoppable with the new Stop API call that provisioners can listen to. --- .../local-exec/resource_provisioner.go | 20 ++++++++++-- .../local-exec/resource_provisioner_test.go | 32 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/builtin/provisioners/local-exec/resource_provisioner.go b/builtin/provisioners/local-exec/resource_provisioner.go index cfd6870104..86f27fb35d 100644 --- a/builtin/provisioners/local-exec/resource_provisioner.go +++ b/builtin/provisioners/local-exec/resource_provisioner.go @@ -68,8 +68,24 @@ func applyFn(ctx context.Context) error { "Executing: %s %s \"%s\"", shell, flag, command)) - // Run the command to completion - err := cmd.Run() + // Start the command + err := cmd.Start() + if err == nil { + // Wait for the command to complete in a goroutine + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + err = cmd.Wait() + }() + + // Wait for the command to finish or for us to be interrupted + select { + case <-doneCh: + case <-ctx.Done(): + cmd.Process.Kill() + err = cmd.Wait() + } + } // Close the write-end of the pipe so that the goroutine mirroring output // ends properly. diff --git a/builtin/provisioners/local-exec/resource_provisioner_test.go b/builtin/provisioners/local-exec/resource_provisioner_test.go index 6d04967ad7..fcc49c01b9 100644 --- a/builtin/provisioners/local-exec/resource_provisioner_test.go +++ b/builtin/provisioners/local-exec/resource_provisioner_test.go @@ -5,6 +5,7 @@ import ( "os" "strings" "testing" + "time" "github.com/hashicorp/terraform/config" "github.com/hashicorp/terraform/terraform" @@ -35,6 +36,37 @@ func TestResourceProvider_Apply(t *testing.T) { } } +func TestResourceProvider_stop(t *testing.T) { + c := testConfig(t, map[string]interface{}{ + "command": "sleep 60", + }) + + output := new(terraform.MockUIOutput) + p := Provisioner() + + var err error + doneCh := make(chan struct{}) + go func() { + defer close(doneCh) + err = p.Apply(output, nil, c) + }() + + select { + case <-doneCh: + t.Fatal("should not finish quickly") + case <-time.After(10 * time.Millisecond): + } + + // Stop it + p.Stop() + + select { + case <-doneCh: + case <-time.After(100 * time.Millisecond): + t.Fatal("should finish") + } +} + func TestResourceProvider_Validate_good(t *testing.T) { c := testConfig(t, map[string]interface{}{ "command": "echo foo",