test/integration/scheduler: fix data races

The plugins get called by scheduler goroutines. At least the polling seems to
be done concurrently and thus needs locking.

Locking the PreBindPlugin state is less obvious. It might be that the scheduler
is really done with the test pod, but that ordering doesn't seem to be enough
for the race detector. It's simpler to add mutex locking.
This commit is contained in:
Patrick Ohly 2023-03-29 20:57:41 +02:00
parent f70c26d495
commit 9e9a6cde4b

View File

@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package plugins contains functional tests for scheduler plugin support.
// Beware that the plugins in this directory are not meant to be used in
// performance tests because they don't behave like real plugins.
package plugins package plugins
import ( import (
"context" "context"
"fmt" "fmt"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -65,7 +67,7 @@ var (
) )
type PreEnqueuePlugin struct { type PreEnqueuePlugin struct {
called int32 called int
admit bool admit bool
} }
@ -76,23 +78,62 @@ type PreFilterPlugin struct {
} }
type ScorePlugin struct { type ScorePlugin struct {
mutex sync.Mutex
failScore bool failScore bool
numScoreCalled int32 numScoreCalled int
highScoreNode string highScoreNode string
} }
func (sp *ScorePlugin) deepCopy() *ScorePlugin {
sp.mutex.Lock()
defer sp.mutex.Unlock()
return &ScorePlugin{
failScore: sp.failScore,
numScoreCalled: sp.numScoreCalled,
highScoreNode: sp.highScoreNode,
}
}
type ScoreWithNormalizePlugin struct { type ScoreWithNormalizePlugin struct {
mutex sync.Mutex
numScoreCalled int numScoreCalled int
numNormalizeScoreCalled int numNormalizeScoreCalled int
} }
func (sp *ScoreWithNormalizePlugin) deepCopy() *ScoreWithNormalizePlugin {
sp.mutex.Lock()
defer sp.mutex.Unlock()
return &ScoreWithNormalizePlugin{
numScoreCalled: sp.numScoreCalled,
numNormalizeScoreCalled: sp.numNormalizeScoreCalled,
}
}
type FilterPlugin struct { type FilterPlugin struct {
numFilterCalled int32 mutex sync.Mutex
numFilterCalled int
failFilter bool failFilter bool
rejectFilter bool rejectFilter bool
numCalledPerPod map[string]int numCalledPerPod map[string]int
sync.RWMutex }
func (fp *FilterPlugin) deepCopy() *FilterPlugin {
fp.mutex.Lock()
defer fp.mutex.Unlock()
clone := &FilterPlugin{
numFilterCalled: fp.numFilterCalled,
failFilter: fp.failFilter,
rejectFilter: fp.rejectFilter,
numCalledPerPod: make(map[string]int),
}
for pod, counter := range fp.numCalledPerPod {
clone.numCalledPerPod[pod] = counter
}
return clone
} }
type PostFilterPlugin struct { type PostFilterPlugin struct {
@ -118,6 +159,7 @@ type PreScorePlugin struct {
} }
type PreBindPlugin struct { type PreBindPlugin struct {
mutex sync.Mutex
numPreBindCalled int numPreBindCalled int
failPreBind bool failPreBind bool
rejectPreBind bool rejectPreBind bool
@ -127,7 +169,34 @@ type PreBindPlugin struct {
podUIDs map[types.UID]struct{} podUIDs map[types.UID]struct{}
} }
func (pp *PreBindPlugin) set(fail, reject, succeed bool) {
pp.mutex.Lock()
defer pp.mutex.Unlock()
pp.failPreBind = fail
pp.rejectPreBind = reject
pp.succeedOnRetry = succeed
}
func (pp *PreBindPlugin) deepCopy() *PreBindPlugin {
pp.mutex.Lock()
defer pp.mutex.Unlock()
clone := &PreBindPlugin{
numPreBindCalled: pp.numPreBindCalled,
failPreBind: pp.failPreBind,
rejectPreBind: pp.rejectPreBind,
succeedOnRetry: pp.succeedOnRetry,
podUIDs: make(map[types.UID]struct{}),
}
for uid := range pp.podUIDs {
clone.podUIDs[uid] = struct{}{}
}
return clone
}
type BindPlugin struct { type BindPlugin struct {
mutex sync.Mutex
name string name string
numBindCalled int numBindCalled int
bindStatus *framework.Status bindStatus *framework.Status
@ -135,13 +204,39 @@ type BindPlugin struct {
pluginInvokeEventChan chan pluginInvokeEvent pluginInvokeEventChan chan pluginInvokeEvent
} }
func (bp *BindPlugin) deepCopy() *BindPlugin {
bp.mutex.Lock()
defer bp.mutex.Unlock()
return &BindPlugin{
name: bp.name,
numBindCalled: bp.numBindCalled,
bindStatus: bp.bindStatus,
client: bp.client,
pluginInvokeEventChan: bp.pluginInvokeEventChan,
}
}
type PostBindPlugin struct { type PostBindPlugin struct {
mutex sync.Mutex
name string name string
numPostBindCalled int numPostBindCalled int
pluginInvokeEventChan chan pluginInvokeEvent pluginInvokeEventChan chan pluginInvokeEvent
} }
func (pp *PostBindPlugin) deepCopy() *PostBindPlugin {
pp.mutex.Lock()
defer pp.mutex.Unlock()
return &PostBindPlugin{
name: pp.name,
numPostBindCalled: pp.numPostBindCalled,
pluginInvokeEventChan: pp.pluginInvokeEventChan,
}
}
type PermitPlugin struct { type PermitPlugin struct {
mutex sync.Mutex
name string name string
numPermitCalled int numPermitCalled int
failPermit bool failPermit bool
@ -156,6 +251,26 @@ type PermitPlugin struct {
fh framework.Handle fh framework.Handle
} }
func (pp *PermitPlugin) deepCopy() *PermitPlugin {
pp.mutex.Lock()
defer pp.mutex.Unlock()
return &PermitPlugin{
name: pp.name,
numPermitCalled: pp.numPermitCalled,
failPermit: pp.failPermit,
rejectPermit: pp.rejectPermit,
timeoutPermit: pp.timeoutPermit,
waitAndRejectPermit: pp.waitAndRejectPermit,
waitAndAllowPermit: pp.waitAndAllowPermit,
cancelled: pp.cancelled,
waitingPod: pp.waitingPod,
rejectingPod: pp.rejectingPod,
allowingPod: pp.allowingPod,
fh: pp.fh,
}
}
const ( const (
enqueuePluginName = "enqueue-plugin" enqueuePluginName = "enqueue-plugin"
prefilterPluginName = "prefilter-plugin" prefilterPluginName = "prefilter-plugin"
@ -216,13 +331,16 @@ func (sp *ScorePlugin) Name() string {
// Score returns the score of scheduling a pod on a specific node. // Score returns the score of scheduling a pod on a specific node.
func (sp *ScorePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) { func (sp *ScorePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
curCalled := atomic.AddInt32(&sp.numScoreCalled, 1) sp.mutex.Lock()
defer sp.mutex.Unlock()
sp.numScoreCalled++
if sp.failScore { if sp.failScore {
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("injecting failure for pod %v", p.Name)) return 0, framework.NewStatus(framework.Error, fmt.Sprintf("injecting failure for pod %v", p.Name))
} }
score := int64(1) score := int64(1)
if curCalled == 1 { if sp.numScoreCalled == 1 {
// The first node is scored the highest, the rest is scored lower. // The first node is scored the highest, the rest is scored lower.
sp.highScoreNode = nodeName sp.highScoreNode = nodeName
score = framework.MaxNodeScore score = framework.MaxNodeScore
@ -241,6 +359,9 @@ func (sp *ScoreWithNormalizePlugin) Name() string {
// Score returns the score of scheduling a pod on a specific node. // Score returns the score of scheduling a pod on a specific node.
func (sp *ScoreWithNormalizePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) { func (sp *ScoreWithNormalizePlugin) Score(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) (int64, *framework.Status) {
sp.mutex.Lock()
defer sp.mutex.Unlock()
sp.numScoreCalled++ sp.numScoreCalled++
score := int64(10) score := int64(10)
return score, nil return score, nil
@ -263,12 +384,12 @@ func (fp *FilterPlugin) Name() string {
// Filter is a test function that returns an error or nil, depending on the // Filter is a test function that returns an error or nil, depending on the
// value of "failFilter". // value of "failFilter".
func (fp *FilterPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status { func (fp *FilterPlugin) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *framework.NodeInfo) *framework.Status {
atomic.AddInt32(&fp.numFilterCalled, 1) fp.mutex.Lock()
defer fp.mutex.Unlock()
fp.numFilterCalled++
if fp.numCalledPerPod != nil { if fp.numCalledPerPod != nil {
fp.Lock()
fp.numCalledPerPod[fmt.Sprintf("%v/%v", pod.Namespace, pod.Name)]++ fp.numCalledPerPod[fmt.Sprintf("%v/%v", pod.Namespace, pod.Name)]++
fp.Unlock()
} }
if fp.failFilter { if fp.failFilter {
@ -328,6 +449,9 @@ func (pp *PreBindPlugin) Name() string {
// PreBind is a test function that returns (true, nil) or errors for testing. // PreBind is a test function that returns (true, nil) or errors for testing.
func (pp *PreBindPlugin) PreBind(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) *framework.Status { func (pp *PreBindPlugin) PreBind(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) *framework.Status {
pp.mutex.Lock()
defer pp.mutex.Unlock()
pp.numPreBindCalled++ pp.numPreBindCalled++
if _, tried := pp.podUIDs[pod.UID]; tried && pp.succeedOnRetry { if _, tried := pp.podUIDs[pod.UID]; tried && pp.succeedOnRetry {
return nil return nil
@ -349,6 +473,9 @@ func (bp *BindPlugin) Name() string {
} }
func (bp *BindPlugin) Bind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status { func (bp *BindPlugin) Bind(ctx context.Context, state *framework.CycleState, p *v1.Pod, nodeName string) *framework.Status {
bp.mutex.Lock()
defer bp.mutex.Unlock()
bp.numBindCalled++ bp.numBindCalled++
if bp.pluginInvokeEventChan != nil { if bp.pluginInvokeEventChan != nil {
bp.pluginInvokeEventChan <- pluginInvokeEvent{pluginName: bp.Name(), val: bp.numBindCalled} bp.pluginInvokeEventChan <- pluginInvokeEvent{pluginName: bp.Name(), val: bp.numBindCalled}
@ -374,6 +501,9 @@ func (pp *PostBindPlugin) Name() string {
// PostBind is a test function, which counts the number of times called. // PostBind is a test function, which counts the number of times called.
func (pp *PostBindPlugin) PostBind(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) { func (pp *PostBindPlugin) PostBind(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) {
pp.mutex.Lock()
defer pp.mutex.Unlock()
pp.numPostBindCalled++ pp.numPostBindCalled++
if pp.pluginInvokeEventChan != nil { if pp.pluginInvokeEventChan != nil {
pp.pluginInvokeEventChan <- pluginInvokeEvent{pluginName: pp.Name(), val: pp.numPostBindCalled} pp.pluginInvokeEventChan <- pluginInvokeEvent{pluginName: pp.Name(), val: pp.numPostBindCalled}
@ -443,6 +573,9 @@ func (pp *PermitPlugin) Name() string {
// Permit implements the permit test plugin. // Permit implements the permit test plugin.
func (pp *PermitPlugin) Permit(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (*framework.Status, time.Duration) { func (pp *PermitPlugin) Permit(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (*framework.Status, time.Duration) {
pp.mutex.Lock()
defer pp.mutex.Unlock()
pp.numPermitCalled++ pp.numPermitCalled++
if pp.failPermit { if pp.failPermit {
return framework.NewStatus(framework.Error, fmt.Sprintf("injecting failure for pod %v", pod.Name)), 0 return framework.NewStatus(framework.Error, fmt.Sprintf("injecting failure for pod %v", pod.Name)), 0
@ -454,6 +587,8 @@ func (pp *PermitPlugin) Permit(ctx context.Context, state *framework.CycleState,
go func() { go func() {
select { select {
case <-ctx.Done(): case <-ctx.Done():
pp.mutex.Lock()
defer pp.mutex.Unlock()
pp.cancelled = true pp.cancelled = true
} }
}() }()
@ -487,6 +622,8 @@ func (pp *PermitPlugin) allowAllPods() {
// rejectAllPods rejects all waiting pods. // rejectAllPods rejects all waiting pods.
func (pp *PermitPlugin) rejectAllPods() { func (pp *PermitPlugin) rejectAllPods() {
pp.mutex.Lock()
defer pp.mutex.Unlock()
pp.fh.IterateOverWaitingPods(func(wp framework.WaitingPod) { wp.Reject(pp.name, "rejectAllPods") }) pp.fh.IterateOverWaitingPods(func(wp framework.WaitingPod) { wp.Reject(pp.name, "rejectAllPods") })
} }
@ -559,18 +696,18 @@ func TestPreFilterPlugin(t *testing.T) {
// TestPostFilterPlugin tests invocation of postFilter plugins. // TestPostFilterPlugin tests invocation of postFilter plugins.
func TestPostFilterPlugin(t *testing.T) { func TestPostFilterPlugin(t *testing.T) {
var numNodes int32 = 1 numNodes := 1
tests := []struct { tests := []struct {
name string name string
numNodes int32 numNodes int
rejectFilter bool rejectFilter bool
failScore bool failScore bool
rejectPostFilter bool rejectPostFilter bool
rejectPostFilter2 bool rejectPostFilter2 bool
breakPostFilter bool breakPostFilter bool
breakPostFilter2 bool breakPostFilter2 bool
expectFilterNumCalled int32 expectFilterNumCalled int
expectScoreNumCalled int32 expectScoreNumCalled int
expectPostFilterNumCalled int expectPostFilterNumCalled int
}{ }{
{ {
@ -712,20 +849,20 @@ func TestPostFilterPlugin(t *testing.T) {
t.Errorf("Didn't expect the pod to be scheduled.") t.Errorf("Didn't expect the pod to be scheduled.")
} }
if numFilterCalled := atomic.LoadInt32(&filterPlugin.numFilterCalled); numFilterCalled < tt.expectFilterNumCalled { if numFilterCalled := filterPlugin.deepCopy().numFilterCalled; numFilterCalled < tt.expectFilterNumCalled {
t.Errorf("Expected the filter plugin to be called at least %v times, but got %v.", tt.expectFilterNumCalled, numFilterCalled) t.Errorf("Expected the filter plugin to be called at least %v times, but got %v.", tt.expectFilterNumCalled, numFilterCalled)
} }
if numScoreCalled := atomic.LoadInt32(&scorePlugin.numScoreCalled); numScoreCalled < tt.expectScoreNumCalled { if numScoreCalled := scorePlugin.deepCopy().numScoreCalled; numScoreCalled < tt.expectScoreNumCalled {
t.Errorf("Expected the score plugin to be called at least %v times, but got %v.", tt.expectScoreNumCalled, numScoreCalled) t.Errorf("Expected the score plugin to be called at least %v times, but got %v.", tt.expectScoreNumCalled, numScoreCalled)
} }
} else { } else {
if err = testutils.WaitForPodToSchedule(testCtx.ClientSet, pod); err != nil { if err = testutils.WaitForPodToSchedule(testCtx.ClientSet, pod); err != nil {
t.Errorf("Expected the pod to be scheduled. error: %v", err) t.Errorf("Expected the pod to be scheduled. error: %v", err)
} }
if numFilterCalled := atomic.LoadInt32(&filterPlugin.numFilterCalled); numFilterCalled != tt.expectFilterNumCalled { if numFilterCalled := filterPlugin.deepCopy().numFilterCalled; numFilterCalled != tt.expectFilterNumCalled {
t.Errorf("Expected the filter plugin to be called %v times, but got %v.", tt.expectFilterNumCalled, numFilterCalled) t.Errorf("Expected the filter plugin to be called %v times, but got %v.", tt.expectFilterNumCalled, numFilterCalled)
} }
if numScoreCalled := atomic.LoadInt32(&scorePlugin.numScoreCalled); numScoreCalled != tt.expectScoreNumCalled { if numScoreCalled := scorePlugin.deepCopy().numScoreCalled; numScoreCalled != tt.expectScoreNumCalled {
t.Errorf("Expected the score plugin to be called %v times, but got %v.", tt.expectScoreNumCalled, numScoreCalled) t.Errorf("Expected the score plugin to be called %v times, but got %v.", tt.expectScoreNumCalled, numScoreCalled)
} }
} }
@ -792,7 +929,7 @@ func TestScorePlugin(t *testing.T) {
} }
} }
if numScoreCalled := atomic.LoadInt32(&scorePlugin.numScoreCalled); numScoreCalled == 0 { if numScoreCalled := scorePlugin.deepCopy().numScoreCalled; numScoreCalled == 0 {
t.Errorf("Expected the score plugin to be called.") t.Errorf("Expected the score plugin to be called.")
} }
}) })
@ -820,10 +957,11 @@ func TestNormalizeScorePlugin(t *testing.T) {
t.Errorf("Expected the pod to be scheduled. error: %v", err) t.Errorf("Expected the pod to be scheduled. error: %v", err)
} }
if scoreWithNormalizePlugin.numScoreCalled == 0 { p := scoreWithNormalizePlugin.deepCopy()
if p.numScoreCalled == 0 {
t.Errorf("Expected the score plugin to be called.") t.Errorf("Expected the score plugin to be called.")
} }
if scoreWithNormalizePlugin.numNormalizeScoreCalled == 0 { if p.numNormalizeScoreCalled == 0 {
t.Error("Expected the normalize score plugin to be called") t.Error("Expected the normalize score plugin to be called")
} }
} }
@ -980,9 +1118,8 @@ func TestPrebindPlugin(t *testing.T) {
} }
} }
preBindPlugin.failPreBind = test.fail preBindPlugin.set(test.fail, test.reject, test.succeedOnRetry)
preBindPlugin.rejectPreBind = test.reject
preBindPlugin.succeedOnRetry = test.succeedOnRetry
// Create a best effort pod. // Create a best effort pod.
pod, err := createPausePod(testCtx.ClientSet, pod, err := createPausePod(testCtx.ClientSet,
initPausePod(&testutils.PausePodConfig{Name: "test-pod", Namespace: testCtx.NS.Name})) initPausePod(&testutils.PausePodConfig{Name: "test-pod", Namespace: testCtx.NS.Name}))
@ -1006,7 +1143,8 @@ func TestPrebindPlugin(t *testing.T) {
t.Errorf("Expected the pod to be scheduled. error: %v", err) t.Errorf("Expected the pod to be scheduled. error: %v", err)
} }
if preBindPlugin.numPreBindCalled == 0 { p := preBindPlugin.deepCopy()
if p.numPreBindCalled == 0 {
t.Errorf("Expected the prebind plugin to be called.") t.Errorf("Expected the prebind plugin to be called.")
} }
@ -1014,7 +1152,7 @@ func TestPrebindPlugin(t *testing.T) {
if err := wait.Poll(10*time.Millisecond, 15*time.Second, func() (bool, error) { if err := wait.Poll(10*time.Millisecond, 15*time.Second, func() (bool, error) {
// 2 means the unschedulable pod is expected to be retried at least twice. // 2 means the unschedulable pod is expected to be retried at least twice.
// (one initial attempt plus the one moved by the preBind pod) // (one initial attempt plus the one moved by the preBind pod)
return int(filterPlugin.numFilterCalled) >= 2*nodesNum, nil return filterPlugin.deepCopy().numFilterCalled >= 2*nodesNum, nil
}); err != nil { }); err != nil {
t.Errorf("Timed out waiting for the unschedulable Pod to be retried at least twice.") t.Errorf("Timed out waiting for the unschedulable Pod to be retried at least twice.")
} }
@ -1523,27 +1661,30 @@ func TestBindPlugin(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("can't get pod: %v", err) t.Errorf("can't get pod: %v", err)
} }
p1 := bindPlugin1.deepCopy()
p2 := bindPlugin2.deepCopy()
if test.expectBoundByScheduler { if test.expectBoundByScheduler {
if pod.Annotations[bindPluginAnnotation] != "" { if pod.Annotations[bindPluginAnnotation] != "" {
t.Errorf("Expected the pod to be bound by scheduler instead of by bindplugin %s", pod.Annotations[bindPluginAnnotation]) t.Errorf("Expected the pod to be bound by scheduler instead of by bindplugin %s", pod.Annotations[bindPluginAnnotation])
} }
if bindPlugin1.numBindCalled != 1 || bindPlugin2.numBindCalled != 1 { if p1.numBindCalled != 1 || p2.numBindCalled != 1 {
t.Errorf("Expected each bind plugin to be called once, was called %d and %d times.", bindPlugin1.numBindCalled, bindPlugin2.numBindCalled) t.Errorf("Expected each bind plugin to be called once, was called %d and %d times.", p1.numBindCalled, p2.numBindCalled)
} }
} else { } else {
if pod.Annotations[bindPluginAnnotation] != test.expectBindPluginName { if pod.Annotations[bindPluginAnnotation] != test.expectBindPluginName {
t.Errorf("Expected the pod to be bound by bindplugin %s instead of by bindplugin %s", test.expectBindPluginName, pod.Annotations[bindPluginAnnotation]) t.Errorf("Expected the pod to be bound by bindplugin %s instead of by bindplugin %s", test.expectBindPluginName, pod.Annotations[bindPluginAnnotation])
} }
if bindPlugin1.numBindCalled != 1 { if p1.numBindCalled != 1 {
t.Errorf("Expected %s to be called once, was called %d times.", bindPlugin1.Name(), bindPlugin1.numBindCalled) t.Errorf("Expected %s to be called once, was called %d times.", p1.Name(), p1.numBindCalled)
} }
if test.expectBindPluginName == bindPlugin1.Name() && bindPlugin2.numBindCalled > 0 { if test.expectBindPluginName == p1.Name() && p2.numBindCalled > 0 {
// expect bindplugin1 succeeded to bind the pod and bindplugin2 should not be called. // expect bindplugin1 succeeded to bind the pod and bindplugin2 should not be called.
t.Errorf("Expected %s not to be called, was called %d times.", bindPlugin2.Name(), bindPlugin1.numBindCalled) t.Errorf("Expected %s not to be called, was called %d times.", p2.Name(), p2.numBindCalled)
} }
} }
if err = wait.Poll(10*time.Millisecond, 30*time.Second, func() (done bool, err error) { if err = wait.Poll(10*time.Millisecond, 30*time.Second, func() (done bool, err error) {
return postBindPlugin.numPostBindCalled == 1, nil p := postBindPlugin.deepCopy()
return p.numPostBindCalled == 1, nil
}); err != nil { }); err != nil {
t.Errorf("Expected the postbind plugin to be called once, was called %d times.", postBindPlugin.numPostBindCalled) t.Errorf("Expected the postbind plugin to be called once, was called %d times.", postBindPlugin.numPostBindCalled)
} }
@ -1555,8 +1696,9 @@ func TestBindPlugin(t *testing.T) {
if err = wait.Poll(10*time.Millisecond, 30*time.Second, podSchedulingError(testCtx.ClientSet, pod.Namespace, pod.Name)); err != nil { if err = wait.Poll(10*time.Millisecond, 30*time.Second, podSchedulingError(testCtx.ClientSet, pod.Namespace, pod.Name)); err != nil {
t.Errorf("Expected a scheduling error, but didn't get it. error: %v", err) t.Errorf("Expected a scheduling error, but didn't get it. error: %v", err)
} }
if postBindPlugin.numPostBindCalled > 0 { p := postBindPlugin.deepCopy()
t.Errorf("Didn't expect the postbind plugin to be called %d times.", postBindPlugin.numPostBindCalled) if p.numPostBindCalled > 0 {
t.Errorf("Didn't expect the postbind plugin to be called %d times.", p.numPostBindCalled)
} }
} }
for j := range test.expectInvokeEvents { for j := range test.expectInvokeEvents {
@ -1732,7 +1874,8 @@ func TestPermitPlugin(t *testing.T) {
} }
} }
if perPlugin.numPermitCalled == 0 { p := perPlugin.deepCopy()
if p.numPermitCalled == 0 {
t.Errorf("Expected the permit plugin to be called.") t.Errorf("Expected the permit plugin to be called.")
} }
}) })
@ -1825,7 +1968,9 @@ func TestPermitPluginsCancelled(t *testing.T) {
perPlugin1.rejectAllPods() perPlugin1.rejectAllPods()
// Wait some time for the permit plugins to be cancelled // Wait some time for the permit plugins to be cancelled
err = wait.Poll(10*time.Millisecond, 30*time.Second, func() (bool, error) { err = wait.Poll(10*time.Millisecond, 30*time.Second, func() (bool, error) {
return perPlugin1.cancelled && perPlugin2.cancelled, nil p1 := perPlugin1.deepCopy()
p2 := perPlugin2.deepCopy()
return p1.cancelled && p2.cancelled, nil
}) })
if err != nil { if err != nil {
t.Errorf("Expected all permit plugins to be cancelled") t.Errorf("Expected all permit plugins to be cancelled")
@ -1910,7 +2055,8 @@ func TestCoSchedulingWithPermitPlugin(t *testing.T) {
} }
} }
if permitPlugin.numPermitCalled == 0 { p := permitPlugin.deepCopy()
if p.numPermitCalled == 0 {
t.Errorf("Expected the permit plugin to be called.") t.Errorf("Expected the permit plugin to be called.")
} }
}) })
@ -2249,9 +2395,8 @@ func TestPreemptWithPermitPlugin(t *testing.T) {
t.Fatalf("Expected the waiting pod to get preempted.") t.Fatalf("Expected the waiting pod to get preempted.")
} }
filterPlugin.RLock() p := filterPlugin.deepCopy()
waitingPodCalled := filterPlugin.numCalledPerPod[fmt.Sprintf("%v/%v", w.Namespace, w.Name)] waitingPodCalled := p.numCalledPerPod[fmt.Sprintf("%v/%v", w.Namespace, w.Name)]
filterPlugin.RUnlock()
if waitingPodCalled > tt.maxNumWaitingPodCalled { if waitingPodCalled > tt.maxNumWaitingPodCalled {
t.Fatalf("Expected the waiting pod to be called %v times at most, but got %v", tt.maxNumWaitingPodCalled, waitingPodCalled) t.Fatalf("Expected the waiting pod to be called %v times at most, but got %v", tt.maxNumWaitingPodCalled, waitingPodCalled)
} }