Skip to content

Commit eb6265a

Browse files
authored
support spread constraints (#168)
Signed-off-by: lihanbo <[email protected]>
1 parent 2643cf0 commit eb6265a

File tree

2 files changed

+98
-3
lines changed

2 files changed

+98
-3
lines changed

pkg/scheduler/core/generic_scheduler.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/karmada-io/karmada/pkg/scheduler/cache"
1313
"github.com/karmada-io/karmada/pkg/scheduler/framework"
1414
"github.com/karmada-io/karmada/pkg/scheduler/framework/runtime"
15+
"github.com/karmada-io/karmada/pkg/util"
1516
)
1617

1718
// ScheduleAlgorithm is the interface that should be implemented to schedule a resource to the target clusters.
@@ -81,7 +82,7 @@ func (g *genericScheduler) Schedule(ctx context.Context, binding *v1alpha1.Propa
8182
}
8283
klog.V(4).Infof("feasible clusters scores for <%s/%s>: %v", binding.Namespace, binding.Name, clustersScore)
8384

84-
clusters := g.selectClusters(clustersScore)
85+
clusters := g.selectClusters(clustersScore, policy.Spec.Placement.SpreadConstraints, feasibleClusters)
8586
result.SuggestedClusters = clusters
8687

8788
return result, nil
@@ -130,11 +131,76 @@ func (g *genericScheduler) prioritizeClusters(
130131
return result, nil
131132
}
132133

133-
// TODO: update the algorithms
134-
func (g *genericScheduler) selectClusters(clustersScore framework.ClusterScoreList) []string {
134+
func (g *genericScheduler) selectClusters(clustersScore framework.ClusterScoreList, spreadConstraints []v1alpha1.SpreadConstraint, clusters []*clusterapi.Cluster) []string {
135+
if len(spreadConstraints) != 0 {
136+
return g.matchSpreadConstraints(clusters, spreadConstraints)
137+
}
138+
135139
out := make([]string, len(clustersScore))
136140
for i := range clustersScore {
137141
out[i] = clustersScore[i].Name
138142
}
139143
return out
140144
}
145+
146+
func (g *genericScheduler) matchSpreadConstraints(clusters []*clusterapi.Cluster, spreadConstraints []v1alpha1.SpreadConstraint) []string {
147+
state := util.NewSpreadGroup()
148+
g.runSpreadConstraintsFilter(clusters, spreadConstraints, state)
149+
return g.calSpreadResult(state)
150+
}
151+
152+
// Now support spread by cluster. More rules will be implemented later.
153+
func (g *genericScheduler) runSpreadConstraintsFilter(clusters []*clusterapi.Cluster, spreadConstraints []v1alpha1.SpreadConstraint, spreadGroup *util.SpreadGroup) {
154+
for _, spreadConstraint := range spreadConstraints {
155+
spreadGroup.InitialGroupRecord(spreadConstraint)
156+
if spreadConstraint.SpreadByField == v1alpha1.SpreadByFieldCluster {
157+
g.groupByFieldCluster(clusters, spreadConstraint, spreadGroup)
158+
}
159+
}
160+
}
161+
162+
func (g *genericScheduler) groupByFieldCluster(clusters []*clusterapi.Cluster, spreadConstraint v1alpha1.SpreadConstraint, spreadGroup *util.SpreadGroup) {
163+
for _, cluster := range clusters {
164+
clusterGroup := cluster.Name
165+
spreadGroup.GroupRecord[spreadConstraint][clusterGroup] = append(spreadGroup.GroupRecord[spreadConstraint][clusterGroup], cluster.Name)
166+
}
167+
}
168+
169+
func (g *genericScheduler) calSpreadResult(spreadGroup *util.SpreadGroup) []string {
170+
// TODO: now support single spread constraint
171+
if len(spreadGroup.GroupRecord) > 1 {
172+
return nil
173+
}
174+
175+
return g.chooseSpreadGroup(spreadGroup)
176+
}
177+
178+
func (g *genericScheduler) chooseSpreadGroup(spreadGroup *util.SpreadGroup) []string {
179+
var feasibleClusters []string
180+
for spreadConstraint, clusterGroups := range spreadGroup.GroupRecord {
181+
if spreadConstraint.SpreadByField == v1alpha1.SpreadByFieldCluster {
182+
if len(clusterGroups) < spreadConstraint.MinGroups {
183+
return nil
184+
}
185+
186+
if len(clusterGroups) <= spreadConstraint.MaxGroups {
187+
for _, v := range clusterGroups {
188+
feasibleClusters = append(feasibleClusters, v...)
189+
}
190+
break
191+
}
192+
193+
if spreadConstraint.MaxGroups > 0 && len(clusterGroups) > spreadConstraint.MaxGroups {
194+
var groups []string
195+
for group := range clusterGroups {
196+
groups = append(groups, group)
197+
}
198+
199+
for i := 0; i < spreadConstraint.MaxGroups; i++ {
200+
feasibleClusters = append(feasibleClusters, clusterGroups[groups[i]]...)
201+
}
202+
}
203+
}
204+
}
205+
return feasibleClusters
206+
}

pkg/util/spreadstate.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package util
2+
3+
import (
4+
"sync"
5+
6+
"github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
7+
)
8+
9+
// SpreadGroup stores the cluster group info for given spread constraints
10+
type SpreadGroup struct {
11+
// The outer map's keys are SpreadConstraint. The values (inner map) of the outer map are maps with string
12+
// keys and []string values. The inner map's key should specify the cluster group name.
13+
GroupRecord map[v1alpha1.SpreadConstraint]map[string][]string
14+
sync.RWMutex
15+
}
16+
17+
// NewSpreadGroup initializes a SpreadGroup
18+
func NewSpreadGroup() *SpreadGroup {
19+
return &SpreadGroup{
20+
GroupRecord: make(map[v1alpha1.SpreadConstraint]map[string][]string),
21+
}
22+
}
23+
24+
// InitialGroupRecord initials a spread state record
25+
func (ss *SpreadGroup) InitialGroupRecord(constraint v1alpha1.SpreadConstraint) {
26+
ss.Lock()
27+
defer ss.Unlock()
28+
ss.GroupRecord[constraint] = make(map[string][]string)
29+
}

0 commit comments

Comments
 (0)