diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go index c9144cb175e..c36528530f9 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go +++ b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption.go @@ -136,10 +136,14 @@ func (pl *DefaultPreemption) calculateNumCandidates(numNodes int32) int32 { return n } +// getOffsetRand is a dedicated random source for GetOffsetAndNumCandidates calls. +// It defaults to rand.Int31n, but is a package variable so it can be overridden to make unit tests deterministic. +var getOffsetRand = rand.Int31n + // GetOffsetAndNumCandidates chooses a random offset and calculates the number // of candidates that should be shortlisted for dry running preemption. func (pl *DefaultPreemption) GetOffsetAndNumCandidates(numNodes int32) (int32, int32) { - return rand.Int31n(numNodes), pl.calculateNumCandidates(numNodes) + return getOffsetRand(numNodes), pl.calculateNumCandidates(numNodes) } // This function is not applicable for out-of-tree preemption plugins that exercise diff --git a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go index a1d87dcd064..df8703c8fa0 100644 --- a/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go +++ b/pkg/scheduler/framework/plugins/defaultpreemption/default_preemption_test.go @@ -1197,7 +1197,7 @@ func TestDryRunPreemption(t *testing.T) { // Using 4 as a seed source to test getOffsetAndNumCandidates() deterministically. // However, we need to do it after informerFactory.WaitforCacheSync() which might // set a seed. - rand.Seed(4) + getOffsetRand = rand.New(rand.NewSource(4)).Int31n var prevNumFilterCalled int32 for cycle, pod := range tt.testPods { state := framework.NewCycleState() @@ -1396,7 +1396,7 @@ func TestSelectBestCandidate(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - rand.Seed(4) + getOffsetRand = rand.New(rand.NewSource(4)).Int31n nodes := make([]*v1.Node, len(tt.nodeNames)) for i, nodeName := range tt.nodeNames { nodes[i] = st.MakeNode().Name(nodeName).Capacity(veryLargeRes).Obj() diff --git a/plugin/pkg/auth/authorizer/node/node_authorizer_test.go b/plugin/pkg/auth/authorizer/node/node_authorizer_test.go index b9383ffddfd..ba3fe9e7b6c 100644 --- a/plugin/pkg/auth/authorizer/node/node_authorizer_test.go +++ b/plugin/pkg/auth/authorizer/node/node_authorizer_test.go @@ -1237,7 +1237,7 @@ func BenchmarkAuthorization(b *testing.B) { }, } - podToAdd, _ := generatePod("testwrite", "ns0", "node0", "default", opts) + podToAdd, _ := generatePod("testwrite", "ns0", "node0", "default", opts, rand.Perm) b.ResetTimer() for _, testWriteContention := range []bool{false, true} { @@ -1338,11 +1338,11 @@ func populate(graph *Graph, nodes []*corev1.Node, pods []*corev1.Pod, pvs []*cor } } -func randomSubset(a, b int) []int { +func randomSubset(a, b int, randPerm func(int) []int) []int { if b < a { b = a } - return rand.Perm(b)[:a] + return randPerm(b)[:a] } // generate creates sample pods and persistent volumes based on the provided options. @@ -1356,7 +1356,7 @@ func generate(opts *sampleDataOpts) ([]*corev1.Node, []*corev1.Pod, []*corev1.Pe attachments := make([]*storagev1.VolumeAttachment, 0, opts.nodes*opts.attachmentsPerNode) slices := make([]*resourceapi.ResourceSlice, 0, opts.nodes*opts.nodeResourceSlicesPerNode) - rand.Seed(12345) + r := rand.New(rand.NewSource(12345)) for n := 0; n < opts.nodes; n++ { nodeName := fmt.Sprintf("node%d", n) @@ -1365,7 +1365,7 @@ func generate(opts *sampleDataOpts) ([]*corev1.Node, []*corev1.Pod, []*corev1.Pe namespace := fmt.Sprintf("ns%d", p%opts.namespaces) svcAccountName := fmt.Sprintf("svcacct%d-%s", p, nodeName) - pod, podPVs := generatePod(name, namespace, nodeName, svcAccountName, opts) + pod, podPVs := generatePod(name, namespace, nodeName, svcAccountName, opts, r.Perm) pods = append(pods, pod) pvs = append(pvs, podPVs...) } @@ -1395,7 +1395,7 @@ func generate(opts *sampleDataOpts) ([]*corev1.Node, []*corev1.Pod, []*corev1.Pe return nodes, pods, pvs, attachments, slices } -func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleDataOpts) (*corev1.Pod, []*corev1.PersistentVolume) { +func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleDataOpts, randPerm func(int) []int) (*corev1.Pod, []*corev1.PersistentVolume) { pvs := make([]*corev1.PersistentVolume, 0, opts.uniquePVCsPerPod+opts.sharedPVCsPerPod) pod := &corev1.Pod{} @@ -1410,7 +1410,7 @@ func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleD }}) } // Choose shared secrets randomly from shared secrets in a namespace. - subset := randomSubset(opts.sharedSecretsPerPod, opts.sharedSecretsPerNamespace) + subset := randomSubset(opts.sharedSecretsPerPod, opts.sharedSecretsPerNamespace, randPerm) for _, i := range subset { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{SecretName: fmt.Sprintf("secret%d-shared", i)}, @@ -1423,7 +1423,7 @@ func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleD }}) } // Choose shared configmaps randomly from shared configmaps in a namespace. - subset = randomSubset(opts.sharedConfigMapsPerPod, opts.sharedConfigMapsPerNamespace) + subset = randomSubset(opts.sharedConfigMapsPerPod, opts.sharedConfigMapsPerNamespace, randPerm) for _, i := range subset { pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: fmt.Sprintf("configmap%d-shared", i)}}, @@ -1470,7 +1470,7 @@ func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleD }) } // Choose shared pvcs randomly from shared pvcs in a namespace. - subset = randomSubset(opts.sharedPVCsPerPod, opts.sharedPVCsPerNamespace) + subset = randomSubset(opts.sharedPVCsPerPod, opts.sharedPVCsPerNamespace, randPerm) for _, i := range subset { pv := &corev1.PersistentVolume{} pv.Name = fmt.Sprintf("pv%d-shared-%s", i, pod.Namespace) diff --git a/staging/src/k8s.io/apimachinery/pkg/util/rand/rand_test.go b/staging/src/k8s.io/apimachinery/pkg/util/rand/rand_test.go index ea4bc66d515..e677aa97502 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/rand/rand_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/rand/rand_test.go @@ -65,10 +65,10 @@ func TestIntn(t *testing.T) { func TestPerm(t *testing.T) { Seed(5) - rand.Seed(5) + r := rand.New(rand.NewSource(5)) for i := 1; i < 20; i++ { actual := Perm(i) - expected := rand.Perm(i) + expected := r.Perm(i) for j := 0; j < i; j++ { if actual[j] != expected[j] { t.Errorf("Perm call result is unexpected") diff --git a/staging/src/k8s.io/apimachinery/pkg/util/wait/wait.go b/staging/src/k8s.io/apimachinery/pkg/util/wait/wait.go index a65d7708185..7379a8d5ac1 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/wait/wait.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/wait/wait.go @@ -80,6 +80,10 @@ func Forever(f func(), period time.Duration) { Until(f, period, NeverStop) } +// jitterRand is a dedicated random source for jitter calculations. +// It defaults to rand.Float64, but is a package variable so it can be overridden to make unit tests deterministic. +var jitterRand = rand.Float64 + // Jitter returns a time.Duration between duration and duration + maxFactor * // duration. // @@ -89,7 +93,7 @@ func Jitter(duration time.Duration, maxFactor float64) time.Duration { if maxFactor <= 0.0 { maxFactor = 1.0 } - wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration)) + wait := duration + time.Duration(jitterRand()*maxFactor*float64(duration)) return wait } diff --git a/staging/src/k8s.io/apimachinery/pkg/util/wait/wait_test.go b/staging/src/k8s.io/apimachinery/pkg/util/wait/wait_test.go index f05decdec41..cd4b8f95361 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/wait/wait_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/wait/wait_test.go @@ -682,7 +682,7 @@ func TestBackoff_Step(t *testing.T) { initial = nil } t.Run(fmt.Sprintf("%#v seed=%d", initial, seed), func(t *testing.T) { - rand.Seed(seed) + jitterRand = rand.New(rand.NewSource(seed)).Float64 for i := 0; i < len(tt.want); i++ { got := initial.Step() t.Logf("[%d]=%s", i, got) diff --git a/test/e2e/storage/utils/utils.go b/test/e2e/storage/utils/utils.go index 37dd26c1ce2..9a40ff43f24 100644 --- a/test/e2e/storage/utils/utils.go +++ b/test/e2e/storage/utils/utils.go @@ -490,9 +490,9 @@ func readFile(content, path string) string { // genBinDataFromSeed generate binData with random seed func genBinDataFromSeed(len int, seed int64) []byte { binData := make([]byte, len) - rand.Seed(seed) + r := rand.New(rand.NewSource(seed)) - _, err := rand.Read(binData) + _, err := r.Read(binData) if err != nil { fmt.Printf("Error: %v\n", err) }