Skip to content

Commit 0a47251

Browse files
author
Ryan Zhang
committed
test
Signed-off-by: Ryan Zhang <[email protected]>
1 parent 17cf340 commit 0a47251

File tree

12 files changed

+927
-165
lines changed

12 files changed

+927
-165
lines changed

.github/.copilot/breadcrumbs/2025-06-13-1500-scheduler-binding-interface-refactor.md

Lines changed: 519 additions & 0 deletions
Large diffs are not rendered by default.

pkg/scheduler/framework/cyclestate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (c *CycleState) HasObsoleteBindingFor(clusterName string) bool {
140140
// IsClusterObsolete
141141

142142
// NewCycleState creates a CycleState.
143-
func NewCycleState(clusters []clusterv1beta1.MemberCluster, obsoleteBindings []*placementv1beta1.ClusterResourceBinding, scheduledOrBoundBindings ...[]*placementv1beta1.ClusterResourceBinding) *CycleState {
143+
func NewCycleState(clusters []clusterv1beta1.MemberCluster, obsoleteBindings []placementv1beta1.BindingObj, scheduledOrBoundBindings ...[]placementv1beta1.BindingObj) *CycleState {
144144
return &CycleState{
145145
store: sync.Map{},
146146
clusters: clusters,

pkg/scheduler/framework/cyclestate_test.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,18 @@ func TestCycleStateBasicOps(t *testing.T) {
6767
},
6868
}
6969

70-
cs := NewCycleState(clusters, obsoleteBindings, scheduledOrBoundBindings)
70+
// Convert concrete bindings to interface slices
71+
obsoleteBindingObjs := make([]placementv1beta1.BindingObj, len(obsoleteBindings))
72+
for i, binding := range obsoleteBindings {
73+
obsoleteBindingObjs[i] = binding
74+
}
75+
76+
scheduledOrBoundBindingObjs := make([]placementv1beta1.BindingObj, len(scheduledOrBoundBindings))
77+
for i, binding := range scheduledOrBoundBindings {
78+
scheduledOrBoundBindingObjs[i] = binding
79+
}
80+
81+
cs := NewCycleState(clusters, obsoleteBindingObjs, scheduledOrBoundBindingObjs)
7182

7283
k, v := "key", "value"
7384
cs.Write(StateKey(k), StateValue(v))
@@ -125,7 +136,17 @@ func TestPrepareScheduledOrBoundBindingsMap(t *testing.T) {
125136
altClusterName: true,
126137
}
127138

128-
scheduleOrBoundBindingsMap := prepareScheduledOrBoundBindingsMap(scheduled, bound)
139+
// Convert concrete bindings to interface slices
140+
scheduledObjs := make([]placementv1beta1.BindingObj, len(scheduled))
141+
for i, binding := range scheduled {
142+
scheduledObjs[i] = binding
143+
}
144+
boundObjs := make([]placementv1beta1.BindingObj, len(bound))
145+
for i, binding := range bound {
146+
boundObjs[i] = binding
147+
}
148+
149+
scheduleOrBoundBindingsMap := prepareScheduledOrBoundBindingsMap(scheduledObjs, boundObjs)
129150
if diff := cmp.Diff(scheduleOrBoundBindingsMap, want); diff != "" {
130151
t.Errorf("preparedScheduledOrBoundBindingsMap() scheduledOrBoundBindingsMap diff (-got, +want): %s", diff)
131152
}
@@ -157,7 +178,13 @@ func TestPrepareObsoleteBindingsMap(t *testing.T) {
157178
altClusterName: true,
158179
}
159180

160-
obsoleteBindingsMap := prepareObsoleteBindingsMap(obsolete)
181+
// Convert concrete bindings to interface slice
182+
obsoleteObjs := make([]placementv1beta1.BindingObj, len(obsolete))
183+
for i, binding := range obsolete {
184+
obsoleteObjs[i] = binding
185+
}
186+
187+
obsoleteBindingsMap := prepareObsoleteBindingsMap(obsoleteObjs)
161188
if diff := cmp.Diff(obsoleteBindingsMap, want); diff != "" {
162189
t.Errorf("prepareObsoleteBindingsMap() obsoleteBindingsMap diff (-got, +want): %s", diff)
163190
}

pkg/scheduler/framework/cyclestateutils.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ import (
2323
// prepareScheduledOrBoundBindingsMap returns a map that allows quick lookup of whether a cluster
2424
// already has a binding of the scheduled or bound state relevant to the current scheduling
2525
// cycle.
26-
func prepareScheduledOrBoundBindingsMap(scheduledOrBoundBindings ...[]*fleetv1beta1.ClusterResourceBinding) map[string]bool {
26+
func prepareScheduledOrBoundBindingsMap(scheduledOrBoundBindings ...[]fleetv1beta1.BindingObj) map[string]bool {
2727
bm := make(map[string]bool)
2828

2929
for _, bindingSet := range scheduledOrBoundBindings {
3030
for _, binding := range bindingSet {
31-
bm[binding.Spec.TargetCluster] = true
31+
bm[binding.GetBindingSpec().TargetCluster] = true
3232
}
3333
}
3434

@@ -37,11 +37,11 @@ func prepareScheduledOrBoundBindingsMap(scheduledOrBoundBindings ...[]*fleetv1be
3737

3838
// prepareObsoleteBindingsMap returns a map that allows quick lookup of whether a cluster
3939
// already has an obsolete binding relevant to the current scheduling cycle.
40-
func prepareObsoleteBindingsMap(obsoleteBindings []*fleetv1beta1.ClusterResourceBinding) map[string]bool {
40+
func prepareObsoleteBindingsMap(obsoleteBindings []fleetv1beta1.BindingObj) map[string]bool {
4141
bm := make(map[string]bool)
4242

4343
for _, binding := range obsoleteBindings {
44-
bm[binding.Spec.TargetCluster] = true
44+
bm[binding.GetBindingSpec().TargetCluster] = true
4545
}
4646

4747
return bm

pkg/scheduler/framework/framework.go

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -357,25 +357,25 @@ func (f *framework) collectClusters(ctx context.Context) ([]clusterv1beta1.Membe
357357
}
358358

359359
// collectBindings lists all bindings associated with a CRP **using the uncached client**.
360-
func (f *framework) collectBindings(ctx context.Context, crpName string) ([]placementv1beta1.ClusterResourceBinding, error) {
360+
func (f *framework) collectBindings(ctx context.Context, crpName string) ([]placementv1beta1.BindingObj, error) {
361361
bindingList := &placementv1beta1.ClusterResourceBindingList{}
362362
labelSelector := labels.SelectorFromSet(labels.Set{placementv1beta1.CRPTrackingLabel: crpName})
363363
// List bindings directly from the API server.
364364
if err := f.uncachedReader.List(ctx, bindingList, &client.ListOptions{LabelSelector: labelSelector}); err != nil {
365365
return nil, controller.NewAPIServerError(false, err)
366366
}
367-
return bindingList.Items, nil
367+
return controller.ConvertCRBArrayToBindingObjs(controller.ConvertCRBItemsToPointers(bindingList.Items)), nil
368368
}
369369

370370
// markAsUnscheduledForAndUpdate marks a binding as unscheduled and updates it.
371-
var markUnscheduledForAndUpdate = func(ctx context.Context, hubClient client.Client, binding *placementv1beta1.ClusterResourceBinding) error {
371+
var markUnscheduledForAndUpdate = func(ctx context.Context, hubClient client.Client, binding placementv1beta1.BindingObj) error {
372372
// Remember the previous unscheduledBinding state so that we might be able to revert this change if this
373373
// cluster is being selected again before the resources are removed from it. Need to do a get and set if
374374
// we add more annotations to the binding.
375-
binding.SetAnnotations(map[string]string{placementv1beta1.PreviousBindingStateAnnotation: string(binding.Spec.State)})
375+
binding.SetAnnotations(map[string]string{placementv1beta1.PreviousBindingStateAnnotation: string(binding.GetBindingSpec().State)})
376376
// Mark the unscheduledBinding as unscheduled which can conflict with the rollout controller which also changes the state of a
377377
// unscheduledBinding from "scheduled" to "bound".
378-
binding.Spec.State = placementv1beta1.BindingStateUnscheduled
378+
binding.GetBindingSpec().State = placementv1beta1.BindingStateUnscheduled
379379
err := hubClient.Update(ctx, binding, &client.UpdateOptions{})
380380
if err == nil {
381381
klog.V(2).InfoS("Marked binding as unscheduled", "clusterResourceBinding", klog.KObj(binding))
@@ -384,7 +384,7 @@ var markUnscheduledForAndUpdate = func(ctx context.Context, hubClient client.Cli
384384
}
385385

386386
// removeFinalizerAndUpdate removes scheduler CRB cleanup finalizer from ClusterResourceBinding and updates it.
387-
var removeFinalizerAndUpdate = func(ctx context.Context, hubClient client.Client, binding *placementv1beta1.ClusterResourceBinding) error {
387+
var removeFinalizerAndUpdate = func(ctx context.Context, hubClient client.Client, binding placementv1beta1.BindingObj) error {
388388
controllerutil.RemoveFinalizer(binding, placementv1beta1.SchedulerCRBCleanupFinalizer)
389389
err := hubClient.Update(ctx, binding, &client.UpdateOptions{})
390390
if err == nil {
@@ -394,7 +394,7 @@ var removeFinalizerAndUpdate = func(ctx context.Context, hubClient client.Client
394394
}
395395

396396
// updateBindings iterates over bindings and updates them using the update function provided.
397-
func (f *framework) updateBindings(ctx context.Context, bindings []*placementv1beta1.ClusterResourceBinding, updateFn func(ctx context.Context, client client.Client, binding *placementv1beta1.ClusterResourceBinding) error) error {
397+
func (f *framework) updateBindings(ctx context.Context, bindings []placementv1beta1.BindingObj, updateFn func(ctx context.Context, client client.Client, binding placementv1beta1.BindingObj) error) error {
398398
// issue all the update requests in parallel
399399
errs, cctx := errgroup.WithContext(ctx)
400400
for _, binding := range bindings {
@@ -428,7 +428,7 @@ func (f *framework) runSchedulingCycleForPickAllPlacementType(
428428
crpName string,
429429
policy placementv1beta1.PolicySnapshotObj,
430430
clusters []clusterv1beta1.MemberCluster,
431-
bound, scheduled, unscheduled, obsolete []*placementv1beta1.ClusterResourceBinding,
431+
bound, scheduled, unscheduled, obsolete []placementv1beta1.BindingObj,
432432
) (result ctrl.Result, err error) {
433433
policyRef := klog.KObj(policy)
434434

@@ -490,8 +490,11 @@ func (f *framework) runSchedulingCycleForPickAllPlacementType(
490490

491491
// With the PickAll placement type, the desired number of clusters to select always matches
492492
// with the count of scheduled + bound bindings.
493+
// Extract updated bindings from patch structs
494+
patchedBindings := controller.ConvertCRBArrayToBindingObjs(patched)
495+
493496
numOfClusters := len(toCreate) + len(patched) + len(scheduled) + len(bound)
494-
if err := f.updatePolicySnapshotStatusFromBindings(ctx, policy, numOfClusters, nil, filtered, toCreate, patched, scheduled, bound); err != nil {
497+
if err := f.updatePolicySnapshotStatusFromBindings(ctx, policy, numOfClusters, nil, filtered, toCreate, patchedBindings, scheduled, bound); err != nil {
495498
klog.ErrorS(err, "Failed to update latest scheduling decisions and condition", "clusterSchedulingPolicySnapshot", policyRef)
496499
return ctrl.Result{}, err
497500
}
@@ -666,7 +669,7 @@ func (f *framework) runFilterPlugins(ctx context.Context, state *CycleState, pol
666669
func (f *framework) manipulateBindings(
667670
ctx context.Context,
668671
policy placementv1beta1.PolicySnapshotObj,
669-
toCreate, toDelete []*placementv1beta1.ClusterResourceBinding,
672+
toCreate, toDelete []placementv1beta1.BindingObj,
670673
toPatch []*bindingWithPatch,
671674
) error {
672675
policyRef := klog.KObj(policy)
@@ -703,7 +706,7 @@ func (f *framework) manipulateBindings(
703706
}
704707

705708
// createBindings creates a list of new bindings.
706-
func (f *framework) createBindings(ctx context.Context, toCreate []*placementv1beta1.ClusterResourceBinding) error {
709+
func (f *framework) createBindings(ctx context.Context, toCreate []placementv1beta1.BindingObj) error {
707710
// issue all the create requests in parallel
708711
errs, cctx := errgroup.WithContext(ctx)
709712
for _, binding := range toCreate {
@@ -720,7 +723,7 @@ func (f *framework) createBindings(ctx context.Context, toCreate []*placementv1b
720723
// The binding already exists, which is fine.
721724
return nil
722725
}
723-
klog.ErrorS(err, "Failed to create a new binding", "clusterResourceBinding", klog.KObj(newBinding))
726+
klog.ErrorS(err, "Failed to create a new binding", "binding", klog.KObj(newBinding))
724727
}
725728
return err
726729
})
@@ -762,7 +765,7 @@ func (f *framework) updatePolicySnapshotStatusFromBindings(
762765
numOfClusters int,
763766
notPicked ScoredClusters,
764767
filtered []*filteredClusterWithStatus,
765-
existing ...[]*placementv1beta1.ClusterResourceBinding,
768+
existing ...[]placementv1beta1.BindingObj,
766769
) error {
767770
policyRef := klog.KObj(policy)
768771

@@ -811,7 +814,7 @@ func (f *framework) runSchedulingCycleForPickNPlacementType(
811814
crpName string,
812815
policy placementv1beta1.PolicySnapshotObj,
813816
clusters []clusterv1beta1.MemberCluster,
814-
bound, scheduled, unscheduled, obsolete []*placementv1beta1.ClusterResourceBinding,
817+
bound, scheduled, unscheduled, obsolete []placementv1beta1.BindingObj,
815818
) (result ctrl.Result, err error) {
816819
policyRef := klog.KObj(policy)
817820

@@ -976,9 +979,15 @@ func (f *framework) runSchedulingCycleForPickNPlacementType(
976979
patched = append(patched, p.updated)
977980
}
978981

982+
// Extract updated bindings from patch structs for status update
983+
patchedForStatus := make([]placementv1beta1.BindingObj, len(toPatch))
984+
for i, p := range toPatch {
985+
patchedForStatus[i] = p.updated
986+
}
987+
979988
// Update policy snapshot status with the latest scheduling decisions and condition.
980989
klog.V(2).InfoS("Updating policy snapshot status", "clusterSchedulingPolicySnapshot", policyRef)
981-
if err := f.updatePolicySnapshotStatusFromBindings(ctx, policy, numOfClusters, notPicked, filtered, toCreate, patched, scheduled, bound); err != nil {
990+
if err := f.updatePolicySnapshotStatusFromBindings(ctx, policy, numOfClusters, notPicked, filtered, toCreate, patchedForStatus, scheduled, bound); err != nil {
982991
klog.ErrorS(err, "Failed to update latest scheduling decisions and condition", "clusterSchedulingPolicySnapshot", policyRef)
983992
return ctrl.Result{}, err
984993
}
@@ -992,7 +1001,7 @@ func (f *framework) runSchedulingCycleForPickNPlacementType(
9921001
// To minimize interruptions, the scheduler picks scheduled bindings first (in any order); if there
9931002
// are still more bindings to trim, the scheduler will move onto bound bindings, and it prefers
9941003
// ones with a lower cluster score and a smaller name (in alphabetical order) .
995-
func (f *framework) downscale(ctx context.Context, scheduled, bound []*placementv1beta1.ClusterResourceBinding, count int) (updatedScheduled, updatedBound []*placementv1beta1.ClusterResourceBinding, err error) {
1004+
func (f *framework) downscale(ctx context.Context, scheduled, bound []placementv1beta1.BindingObj, count int) (updatedScheduled, updatedBound []placementv1beta1.BindingObj, err error) {
9961005
if count == 0 {
9971006
// Skip if the downscale count is zero.
9981007
return scheduled, bound, nil
@@ -1020,18 +1029,15 @@ func (f *framework) downscale(ctx context.Context, scheduled, bound []*placement
10201029
sortedScheduled := sortByClusterScoreAndName(scheduled)
10211030

10221031
// Trim scheduled bindings.
1023-
bindingsToDelete := make([]*placementv1beta1.ClusterResourceBinding, 0, count)
1024-
for i := 0; i < len(sortedScheduled) && i < count; i++ {
1025-
bindingsToDelete = append(bindingsToDelete, sortedScheduled[i])
1026-
}
1032+
bindingsToDelete := sortedScheduled[:count]
10271033

10281034
return sortedScheduled[count:], bound, f.updateBindings(ctx, bindingsToDelete, markUnscheduledForAndUpdate)
10291035
case count == len(scheduled):
10301036
// Trim all scheduled bindings.
10311037
return nil, bound, f.updateBindings(ctx, scheduled, markUnscheduledForAndUpdate)
10321038
case count < len(scheduled)+len(bound):
10331039
// Trim all scheduled bindings and part of bound bindings.
1034-
bindingsToDelete := make([]*placementv1beta1.ClusterResourceBinding, 0, count)
1040+
bindingsToDelete := make([]placementv1beta1.BindingObj, 0, count)
10351041
bindingsToDelete = append(bindingsToDelete, scheduled...)
10361042

10371043
left := count - len(bindingsToDelete)
@@ -1045,14 +1051,12 @@ func (f *framework) downscale(ctx context.Context, scheduled, bound []*placement
10451051
// Note that this is at best an approximation, as the cluster score assigned earlier might
10461052
// no longer apply, due to the ever-changing state in the fleet.
10471053
sortedBound := sortByClusterScoreAndName(bound)
1048-
for i := 0; i < left && i < len(sortedBound); i++ {
1049-
bindingsToDelete = append(bindingsToDelete, sortedBound[i])
1050-
}
1054+
bindingsToDelete = append(bindingsToDelete, sortedBound[:left]...)
10511055

10521056
return nil, sortedBound[left:], f.updateBindings(ctx, bindingsToDelete, markUnscheduledForAndUpdate)
10531057
case count == len(scheduled)+len(bound):
10541058
// Trim all scheduled and bound bindings.
1055-
bindingsToDelete := make([]*placementv1beta1.ClusterResourceBinding, 0, count)
1059+
bindingsToDelete := make([]placementv1beta1.BindingObj, 0, count)
10561060
bindingsToDelete = append(bindingsToDelete, scheduled...)
10571061
bindingsToDelete = append(bindingsToDelete, bound...)
10581062
return nil, nil, f.updateBindings(ctx, bindingsToDelete, markUnscheduledForAndUpdate)
@@ -1404,7 +1408,7 @@ func (f *framework) runSchedulingCycleForPickFixedPlacementType(
14041408
crpName string,
14051409
policy placementv1beta1.PolicySnapshotObj,
14061410
clusters []clusterv1beta1.MemberCluster,
1407-
bound, scheduled, unscheduled, obsolete []*placementv1beta1.ClusterResourceBinding,
1411+
bound, scheduled, unscheduled, obsolete []placementv1beta1.BindingObj,
14081412
) (ctrl.Result, error) {
14091413
policyRef := klog.KObj(policy)
14101414

0 commit comments

Comments
 (0)