in pkg/scheduler/framework/plugins/topologyspreadconstraints/utils.go [186:297]
func evaluateAllConstraints(
state framework.CycleStatePluginReadWriter,
doNotSchedule, scheduleAnyway []*placementv1beta1.TopologySpreadConstraint,
) (violations doNotScheduleViolations, scores topologySpreadScores, err error) {
violations = make(doNotScheduleViolations)
// Note that this function guarantees that all clusters that do not lead to violations of
// DoNotSchedule topology spread constraints will be assigned a score, even if none of the
// topology spread constraints concerns these clusters.
scores = make(topologySpreadScores)
clusters := state.ListClusters()
for _, constraint := range doNotSchedule {
domainCounter := countByDomain(clusters, state, constraint.TopologyKey)
for _, cluster := range clusters {
if _, ok := violations[clusterName(cluster.Name)]; ok {
// The cluster has violated a DoNotSchedule topology spread constraint; no need
// evaluate other constraints for this cluster.
continue
}
val, ok := cluster.Labels[constraint.TopologyKey]
if !ok {
// The cluster under inspection does not have the topology key and thus is not part
// of the spread.
//
// Placing resources on such clusters will not lead to topology spread constraint
// violations.
//
// Assign a score even if the constraint does not concern the cluster.
scores[clusterName(cluster.Name)] += 0
continue
}
// The cluster under inspection is part of the spread.
// Verify if the placement will violate the constraint.
// The default value for maxSkew is 1.
maxSkew := 1
if constraint.MaxSkew != nil {
maxSkew = int(*constraint.MaxSkew)
}
violated, skewChange, err := willViolate(domainCounter, domainName(val), maxSkew)
if err != nil {
return nil, nil, fmt.Errorf("failed to evaluate DoNotSchedule topology spread constraints: %w", err)
}
if violated {
// A violation happens.
reasons := violationReasons{
fmt.Sprintf(doNotScheduleConstraintViolationReasonTemplate, constraint.TopologyKey, *constraint.MaxSkew),
}
violations[clusterName(cluster.Name)] = reasons
// Untrack the cluster's score.
delete(scores, clusterName(cluster.Name))
continue
}
scores[clusterName(cluster.Name)] += skewChange * int32(skewChangeScoreFactor)
}
}
for _, constraint := range scheduleAnyway {
domainCounter := countByDomain(clusters, state, constraint.TopologyKey)
for _, cluster := range clusters {
if _, ok := violations[clusterName(cluster.Name)]; ok {
// The cluster has violated a DoNotSchedule topology spread constraint; no need
// evaluate other constraints for this cluster.
continue
}
val, ok := cluster.Labels[constraint.TopologyKey]
if !ok {
// The cluster under inspection does not have the topology key and thus is not part
// of the spread.
//
// Placing resources on such clusters will not lead to topology spread constraint
// violations.
//
// Assign a score even if the constraint does not concern the cluster.
scores[clusterName(cluster.Name)] += 0
continue
}
// The cluster under inspection is part of the spread.
// Verify if the placement will violate the constraint.
// The default value for maxSkew is 1.
maxSkew := 1
if constraint.MaxSkew != nil {
maxSkew = int(*constraint.MaxSkew)
}
violated, skewChange, err := willViolate(domainCounter, domainName(val), maxSkew)
if err != nil {
return nil, nil, fmt.Errorf("failed to evaluate ScheduleAnyway topology spread constraints: %w", err)
}
if violated {
// A violation happens; since this is a ScheduleAnyway topology spread constraint,
// a violation score penalty is applied to the score.
scores[clusterName(cluster.Name)] -= maxSkewViolationPenality
continue
}
scores[clusterName(cluster.Name)] += skewChange * int32(skewChangeScoreFactor)
}
}
return violations, scores, nil
}