func evaluateAllConstraints()

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
}