refactor(pkg/descheduler): start factory informers and other descheduler parts during bootstrap

This will help with testing as the initDescheduler function under
descheduler_test.go is now much closer to the production invocation.
This commit is contained in:
Jan Chaloupka
2026-02-03 21:33:28 +01:00
parent 0c307cf7b9
commit e402adb9f1
2 changed files with 50 additions and 47 deletions
+49 -38
View File
@@ -552,6 +552,7 @@ func bootstrapDescheduler(
rs *options.DeschedulerServer,
deschedulerPolicy *api.DeschedulerPolicy,
evictionPolicyGroupVersion string,
metricProviderTokenReconciliation tokenReconciliation,
sharedInformerFactory, namespacedSharedInformerFactory informers.SharedInformerFactory,
eventRecorder events.EventRecorder,
) (*descheduler, error) {
@@ -585,6 +586,53 @@ func bootstrapDescheduler(
}
}
// init is responsible for starting all informer factories, metrics providers
// and other parts that require to start before a first descheduling cycle is run
deschedulerInitFnc := func(ctx context.Context) error {
// In dry run mode, start and sync the fake shared informer factory so it can mirror
// events from the real factory. Reliable propagation depends on both factories being
// fully synced (see WaitForCacheSync calls below), not solely on startup order.
if rs.DryRun {
descheduler.kubeClientSandbox.fakeSharedInformerFactory().Start(ctx.Done())
descheduler.kubeClientSandbox.fakeSharedInformerFactory().WaitForCacheSync(ctx.Done())
}
sharedInformerFactory.Start(ctx.Done())
if metricProviderTokenReconciliation == secretReconciliation {
namespacedSharedInformerFactory.Start(ctx.Done())
}
sharedInformerFactory.WaitForCacheSync(ctx.Done())
if metricProviderTokenReconciliation == secretReconciliation {
namespacedSharedInformerFactory.WaitForCacheSync(ctx.Done())
}
descheduler.podEvictor.WaitForEventHandlersSync(ctx)
if descheduler.metricsCollector != nil {
go func() {
klog.V(2).Infof("Starting metrics collector")
descheduler.metricsCollector.Run(ctx)
klog.V(2).Infof("Stopped metrics collector")
}()
klog.V(2).Infof("Waiting for metrics collector to sync")
if err := wait.PollWithContext(ctx, time.Second, time.Minute, func(context.Context) (done bool, err error) {
return descheduler.metricsCollector.HasSynced(), nil
}); err != nil {
return fmt.Errorf("unable to wait for metrics collector to sync: %v", err)
}
}
if metricProviderTokenReconciliation == secretReconciliation {
go descheduler.promClientCtrl.runAuthenticationSecretReconciler(ctx)
}
return nil
}
if err := deschedulerInitFnc(ctx); err != nil {
return nil, err
}
return descheduler, nil
}
@@ -622,49 +670,12 @@ func RunDeschedulerStrategies(ctx context.Context, rs *options.DeschedulerServer
}
}
descheduler, err := bootstrapDescheduler(ctx, rs, deschedulerPolicy, evictionPolicyGroupVersion, sharedInformerFactory, namespacedSharedInformerFactory, eventRecorder)
descheduler, err := bootstrapDescheduler(ctx, rs, deschedulerPolicy, evictionPolicyGroupVersion, metricProviderTokenReconciliation, sharedInformerFactory, namespacedSharedInformerFactory, eventRecorder)
if err != nil {
span.AddEvent("Failed to bootstrap a descheduler", trace.WithAttributes(attribute.String("err", err.Error())))
return err
}
// In dry run mode, start and sync the fake shared informer factory so it can mirror
// events from the real factory. Reliable propagation depends on both factories being
// fully synced (see WaitForCacheSync calls below), not solely on startup order.
if rs.DryRun {
descheduler.kubeClientSandbox.fakeSharedInformerFactory().Start(ctx.Done())
descheduler.kubeClientSandbox.fakeSharedInformerFactory().WaitForCacheSync(ctx.Done())
}
sharedInformerFactory.Start(ctx.Done())
if metricProviderTokenReconciliation == secretReconciliation {
namespacedSharedInformerFactory.Start(ctx.Done())
}
sharedInformerFactory.WaitForCacheSync(ctx.Done())
if metricProviderTokenReconciliation == secretReconciliation {
namespacedSharedInformerFactory.WaitForCacheSync(ctx.Done())
}
descheduler.podEvictor.WaitForEventHandlersSync(ctx)
if descheduler.metricsCollector != nil {
go func() {
klog.V(2).Infof("Starting metrics collector")
descheduler.metricsCollector.Run(ctx)
klog.V(2).Infof("Stopped metrics collector")
}()
klog.V(2).Infof("Waiting for metrics collector to sync")
if err := wait.PollWithContext(ctx, time.Second, time.Minute, func(context.Context) (done bool, err error) {
return descheduler.metricsCollector.HasSynced(), nil
}); err != nil {
return fmt.Errorf("unable to wait for metrics collector to sync: %v", err)
}
}
if metricProviderTokenReconciliation == secretReconciliation {
go descheduler.promClientCtrl.runAuthenticationSecretReconciler(ctx)
}
wait.NonSlidingUntil(func() {
if metricProviderTokenReconciliation == inClusterReconciliation {
// Read the sa token and assume it has the sufficient permissions to authenticate
+1 -9
View File
@@ -220,20 +220,12 @@ func initDescheduler(t *testing.T, ctx context.Context, featureGates featuregate
eventBroadcaster, eventRecorder := utils.GetRecorderAndBroadcaster(ctx, client)
// Always create descheduler with real client/factory first to register all informers
descheduler, err := bootstrapDescheduler(ctx, rs, internalDeschedulerPolicy, "v1", sharedInformerFactory, nil, eventRecorder)
descheduler, err := bootstrapDescheduler(ctx, rs, internalDeschedulerPolicy, "v1", noReconciliation, sharedInformerFactory, nil, eventRecorder)
if err != nil {
eventBroadcaster.Shutdown()
t.Fatalf("Failed to bootstrap a descheduler: %v", err)
}
// Start the real shared informer factory after creating the descheduler
if dryRun {
descheduler.kubeClientSandbox.fakeSharedInformerFactory().Start(ctx.Done())
descheduler.kubeClientSandbox.fakeSharedInformerFactory().WaitForCacheSync(ctx.Done())
}
sharedInformerFactory.Start(ctx.Done())
sharedInformerFactory.WaitForCacheSync(ctx.Done())
if dryRun {
if err := wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) {
for _, obj := range objects {