|  | //go:build !linux | 
|  | // +build !linux | 
|  |  | 
|  | package tfexec | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "os/exec" | 
|  | "strings" | 
|  | "sync" | 
|  | ) | 
|  |  | 
|  | func (tf *Terraform) runTerraformCmd(ctx context.Context, cmd *exec.Cmd) error { | 
|  | var errBuf strings.Builder | 
|  |  | 
|  | // check for early cancellation | 
|  | select { | 
|  | case <-ctx.Done(): | 
|  | return ctx.Err() | 
|  | default: | 
|  | } | 
|  |  | 
|  | // Read stdout / stderr logs from pipe instead of setting cmd.Stdout and | 
|  | // cmd.Stderr because it can cause hanging when killing the command | 
|  | // https://github.com/golang/go/issues/23019 | 
|  | stdoutWriter := mergeWriters(cmd.Stdout, tf.stdout) | 
|  | stderrWriter := mergeWriters(tf.stderr, &errBuf) | 
|  |  | 
|  | cmd.Stderr = nil | 
|  | cmd.Stdout = nil | 
|  |  | 
|  | stdoutPipe, err := cmd.StdoutPipe() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | stderrPipe, err := cmd.StderrPipe() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  |  | 
|  | err = cmd.Start() | 
|  | if err == nil && ctx.Err() != nil { | 
|  | err = ctx.Err() | 
|  | } | 
|  | if err != nil { | 
|  | return tf.wrapExitError(ctx, err, "") | 
|  | } | 
|  |  | 
|  | var errStdout, errStderr error | 
|  | var wg sync.WaitGroup | 
|  | wg.Add(1) | 
|  | go func() { | 
|  | defer wg.Done() | 
|  | errStdout = writeOutput(ctx, stdoutPipe, stdoutWriter) | 
|  | }() | 
|  |  | 
|  | wg.Add(1) | 
|  | go func() { | 
|  | defer wg.Done() | 
|  | errStderr = writeOutput(ctx, stderrPipe, stderrWriter) | 
|  | }() | 
|  |  | 
|  | // Reads from pipes must be completed before calling cmd.Wait(). Otherwise | 
|  | // can cause a race condition | 
|  | wg.Wait() | 
|  |  | 
|  | err = cmd.Wait() | 
|  | if err == nil && ctx.Err() != nil { | 
|  | err = ctx.Err() | 
|  | } | 
|  | if err != nil { | 
|  | return tf.wrapExitError(ctx, err, errBuf.String()) | 
|  | } | 
|  |  | 
|  | // Return error if there was an issue reading the std out/err | 
|  | if errStdout != nil && ctx.Err() != nil { | 
|  | return tf.wrapExitError(ctx, errStdout, errBuf.String()) | 
|  | } | 
|  | if errStderr != nil && ctx.Err() != nil { | 
|  | return tf.wrapExitError(ctx, errStderr, errBuf.String()) | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } |