Skip to content

Commit

Permalink
Merge pull request #16173 from fuweid/fix-datarace-in-expect
Browse files Browse the repository at this point in the history
pkg/expect: fix data race
  • Loading branch information
ahrtr authored Jul 13, 2023
2 parents 1ee6be7 + 56edfa6 commit 1cf49e5
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 1 deletion.
24 changes: 23 additions & 1 deletion pkg/expect/expect.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type ExpectProcess struct {
fpty *os.File
wg sync.WaitGroup

readCloseCh chan struct{} // close it if async read goroutine exits

mu sync.Mutex // protects lines, count, cur, exitErr and exitCode
lines []string
count int // increment whenever new line gets added
Expand All @@ -67,6 +69,7 @@ func NewExpectWithEnv(name string, args []string, env []string, serverProcessCon
args: args,
env: env,
},
readCloseCh: make(chan struct{}),
}
ep.cmd = commandFromConfig(ep.cfg)

Expand Down Expand Up @@ -100,7 +103,10 @@ func (ep *ExpectProcess) Pid() int {
}

func (ep *ExpectProcess) read() {
defer ep.wg.Done()
defer func() {
ep.wg.Done()
close(ep.readCloseCh)
}()
defer func(fpty *os.File) {
err := fpty.Close()
if err != nil {
Expand Down Expand Up @@ -187,9 +193,25 @@ func (ep *ExpectProcess) ExpectFunc(ctx context.Context, f func(string) bool) (s
}
}

select {
// NOTE: we wait readCloseCh for ep.read() to complete draining the log before acquring the lock.
case <-ep.readCloseCh:
case <-ctx.Done():
return "", fmt.Errorf("failed to find match string: %w", ctx.Err())
}

ep.mu.Lock()
defer ep.mu.Unlock()

// retry it since we get all the log data
for i < len(ep.lines) {
line := ep.lines[i]
i++
if f(line) {
return line, nil
}
}

lastLinesIndex := len(ep.lines) - DEBUG_LINES_TAIL
if lastLinesIndex < 0 {
lastLinesIndex = 0
Expand Down
8 changes: 8 additions & 0 deletions pkg/expect/expect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,11 @@ func TestExitCodeAfterKill(t *testing.T) {
assert.Equal(t, 137, code)
assert.NoError(t, err)
}

func TestExpectForFailFastCommand(t *testing.T) {
ep, err := NewExpect("sh", "-c", `echo "curl: (59) failed setting cipher list"; exit 59`)
require.NoError(t, err)

_, err = ep.Expect("failed setting cipher list")
require.NoError(t, err)
}

0 comments on commit 1cf49e5

Please sign in to comment.