Skip to content

Commit 3401372

Browse files
authored
Merge pull request #274 from artemvmin/concurrent-fake-exec
Make FakeExec safe for concurrency
2 parents 391b47c + fad585f commit 3401372

File tree

1 file changed

+22
-11
lines changed

1 file changed

+22
-11
lines changed

exec/testing/fake_exec.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"io"
23+
"sync"
2324

2425
"k8s.io/utils/exec"
2526
)
@@ -32,30 +33,28 @@ type FakeExec struct {
3233
// ExactOrder enforces that commands are called in the order they are scripted,
3334
// and with the exact same arguments
3435
ExactOrder bool
35-
// DisableScripts removes the requirement that a slice of FakeCommandAction be
36-
// populated before calling Command(). This makes the fakeexec (and subsequent
37-
// calls to Run() or CombinedOutput() always return success and there is no
38-
// ability to set their output.
36+
// DisableScripts removes the requirement that CommandScripts be populated
37+
// before calling Command(). This makes Command() and subsequent calls to
38+
// Run() or CombinedOutput() always return success and empty output.
3939
DisableScripts bool
40+
41+
mu sync.Mutex
4042
}
4143

4244
var _ exec.Interface = &FakeExec{}
4345

4446
// FakeCommandAction is the function to be executed
4547
type FakeCommandAction func(cmd string, args ...string) exec.Cmd
4648

47-
// Command is to track the commands that are executed
49+
// Command returns the next unexecuted command in CommandScripts.
50+
// This function is safe for concurrent access as long as the underlying
51+
// FakeExec struct is not modified during execution.
4852
func (fake *FakeExec) Command(cmd string, args ...string) exec.Cmd {
4953
if fake.DisableScripts {
5054
fakeCmd := &FakeCmd{DisableScripts: true}
5155
return InitFakeCmd(fakeCmd, cmd, args...)
5256
}
53-
if fake.CommandCalls > len(fake.CommandScript)-1 {
54-
panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args))
55-
}
56-
i := fake.CommandCalls
57-
fake.CommandCalls++
58-
fakeCmd := fake.CommandScript[i](cmd, args...)
57+
fakeCmd := fake.nextCommand(cmd, args)
5958
if fake.ExactOrder {
6059
argv := append([]string{cmd}, args...)
6160
fc := fakeCmd.(*FakeCmd)
@@ -74,6 +73,18 @@ func (fake *FakeExec) Command(cmd string, args ...string) exec.Cmd {
7473
return fakeCmd
7574
}
7675

76+
func (fake *FakeExec) nextCommand(cmd string, args []string) exec.Cmd {
77+
fake.mu.Lock()
78+
defer fake.mu.Unlock()
79+
80+
if fake.CommandCalls > len(fake.CommandScript)-1 {
81+
panic(fmt.Sprintf("ran out of Command() actions. Could not handle command [%d]: %s args: %v", fake.CommandCalls, cmd, args))
82+
}
83+
i := fake.CommandCalls
84+
fake.CommandCalls++
85+
return fake.CommandScript[i](cmd, args...)
86+
}
87+
7788
// CommandContext wraps arguments into exec.Cmd
7889
func (fake *FakeExec) CommandContext(ctx context.Context, cmd string, args ...string) exec.Cmd {
7990
return fake.Command(cmd, args...)

0 commit comments

Comments
 (0)