From 44cb98e04fbfaf9c0dcaec4db7858dcdeb54b530 Mon Sep 17 00:00:00 2001 From: Subba Rao Pasupuleti Date: Tue, 5 Dec 2017 12:26:40 -0500 Subject: [PATCH] provisioner/salt-masterless: Wait for operations to complete Previously the provisioner did not wait until the Salt operation had completed before returning, causing some operations not to be applied, and causing the output to get swallowed. Now we wait until the remote work is complete, and copy output into the Terraform log in a similar way as is done for other provisioners. --- .../salt-masterless/resource_provisioner.go | 75 +++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/builtin/provisioners/salt-masterless/resource_provisioner.go b/builtin/provisioners/salt-masterless/resource_provisioner.go index b81a3703789c..70942c5d7b6b 100644 --- a/builtin/provisioners/salt-masterless/resource_provisioner.go +++ b/builtin/provisioners/salt-masterless/resource_provisioner.go @@ -10,6 +10,7 @@ import ( "context" "errors" "fmt" + "io" "os" "path/filepath" @@ -17,6 +18,7 @@ import ( "github.com/hashicorp/terraform/communicator/remote" "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" + linereader "github.com/mitchellh/go-linereader" ) type provisionFn func(terraform.UIOutput, communicator.Communicator) error @@ -139,14 +141,46 @@ func applyFn(ctx context.Context) error { } o.Output(fmt.Sprintf("Downloading saltstack bootstrap to /tmp/install_salt.sh")) if err = comm.Start(cmd); err != nil { - return fmt.Errorf("Unable to download Salt: %s", err) + err = fmt.Errorf("Unable to download Salt: %s", err) } + + if err == nil { + cmd.Wait() + if cmd.ExitStatus != 0 { + err = fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus) + } + } + + outR, outW := io.Pipe() + errR, errW := io.Pipe() + outDoneCh := make(chan struct{}) + errDoneCh := make(chan struct{}) + go copyOutput(o, outR, outDoneCh) + go copyOutput(o, errR, errDoneCh) cmd = &remote.Cmd{ Command: fmt.Sprintf("%s /tmp/install_salt.sh %s", p.sudo("sh"), p.BootstrapArgs), + Stdout: outW, + Stderr: errW, } + o.Output(fmt.Sprintf("Installing Salt with command %s", cmd.Command)) if err = comm.Start(cmd); err != nil { - return fmt.Errorf("Unable to install Salt: %s", err) + err = fmt.Errorf("Unable to install Salt: %s", err) + } + + if err == nil { + cmd.Wait() + if cmd.ExitStatus != 0 { + err = fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus) + } + } + // Wait for output to clean up + outW.Close() + errW.Close() + <-outDoneCh + <-errDoneCh + if err != nil { + return err } } @@ -212,17 +246,39 @@ func applyFn(ctx context.Context) error { } } + outR, outW := io.Pipe() + errR, errW := io.Pipe() + outDoneCh := make(chan struct{}) + errDoneCh := make(chan struct{}) + + go copyOutput(o, outR, outDoneCh) + go copyOutput(o, errR, errDoneCh) o.Output(fmt.Sprintf("Running: salt-call --local %s", p.CmdArgs)) - cmd := &remote.Cmd{Command: p.sudo(fmt.Sprintf("salt-call --local %s", p.CmdArgs))} + cmd := &remote.Cmd{ + Command: p.sudo(fmt.Sprintf("salt-call --local %s", p.CmdArgs)), + Stdout: outW, + Stderr: errW, + } if err = comm.Start(cmd); err != nil || cmd.ExitStatus != 0 { if err == nil { err = fmt.Errorf("Bad exit status: %d", cmd.ExitStatus) } - return fmt.Errorf("Error executing salt-call: %s", err) + err = fmt.Errorf("Error executing salt-call: %s", err) + } + if err == nil { + cmd.Wait() + if cmd.ExitStatus != 0 { + err = fmt.Errorf("Script exited with non-zero exit status: %d", cmd.ExitStatus) + } } + // Wait for output to clean up + outW.Close() + errW.Close() + <-outDoneCh + <-errDoneCh - return nil + return err } // Prepends sudo to supplied command if config says to @@ -466,3 +522,12 @@ func decodeConfig(d *schema.ResourceData) (*provisioner, error) { return p, nil } + +func copyOutput( + o terraform.UIOutput, r io.Reader, doneCh chan<- struct{}) { + defer close(doneCh) + lr := linereader.New(r) + for line := range lr.Ch { + o.Output(line) + } +}