communicator/winrm: fix data race in io copy

As the command completes, the winrm.Command does not wait for its copy of
the remote.Command streams to report itself completed.

This adds an additional sync.WaitGroup to ensure that copy is finished up
before moving on, solving the data race.

Fixes the following data race:

```
==================
WARNING: DATA RACE
Read by goroutine 6:
  github.com/hashicorp/terraform/communicator/winrm.TestStart()
      /Users/phinze/go/src/github.com/hashicorp/terraform/communicator/winrm/communicator_test.go:79 +0xa44
  testing.tRunner()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:456 +0xdc

Previous write by goroutine 14:
  bytes.(*Buffer).ReadFrom()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/bytes/buffer.go:174 +0x465
  io.copyBuffer()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/io/io.go:375 +0x1a5
  io.Copy()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/io/io.go:351 +0x78

Goroutine 6 (running) created at:
  testing.RunTests()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:561 +0xaa3
  testing.(*M).Run()
      /private/var/folders/vd/7l9ys5k57l91x63sh28wl_kc0000gn/T/workdir/go/src/testing/testing.go:494 +0xe4
  main.main()
      github.com/hashicorp/terraform/communicator/winrm/_test/_testmain.go:62 +0x20f

Goroutine 14 (finished) created at:
  github.com/hashicorp/terraform/communicator/winrm.runCommand()
      /Users/phinze/go/src/github.com/hashicorp/terraform/communicator/winrm/communicator.go:151 +0xf8
==================
```
This commit is contained in:
Paul Hinze 2016-01-16 13:33:24 -05:00
parent ead4865b7f
commit 9cbaacad32
1 changed files with 13 additions and 2 deletions

View File

@ -7,6 +7,7 @@ import (
"math/rand"
"strconv"
"strings"
"sync"
"time"
"github.com/hashicorp/terraform/communicator/remote"
@ -148,10 +149,20 @@ func (c *Communicator) Start(rc *remote.Cmd) error {
func runCommand(shell *winrm.Shell, cmd *winrm.Command, rc *remote.Cmd) {
defer shell.Close()
go io.Copy(rc.Stdout, cmd.Stdout)
go io.Copy(rc.Stderr, cmd.Stderr)
var wg sync.WaitGroup
go func() {
wg.Add(1)
io.Copy(rc.Stdout, cmd.Stdout)
wg.Done()
}()
go func() {
wg.Add(1)
io.Copy(rc.Stderr, cmd.Stderr)
wg.Done()
}()
cmd.Wait()
wg.Wait()
rc.SetExited(cmd.ExitCode())
}