package spreadconstraint import ( clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" "github.com/karmada-io/karmada/pkg/scheduler/framework" ) // GroupClustersInfo indicate the cluster global view type GroupClustersInfo struct { Providers map[string]ProviderInfo Regions map[string]RegionInfo Zones map[string]ZoneInfo // Clusters from global view, sorted by cluster.Score descending. Clusters []ClusterDetailInfo calAvailableReplicasFunc func(clusters []*clusterv1alpha1.Cluster, spec *workv1alpha2.ResourceBindingSpec) []workv1alpha2.TargetCluster } // ProviderInfo indicate the provider information type ProviderInfo struct { Name string Score int64 // the highest score in all clusters of the provider AvailableReplicas int64 // Regions under this provider Regions map[string]struct{} // Zones under this provider Zones map[string]struct{} // Clusters under this provider, sorted by cluster.Score descending. Clusters []ClusterDetailInfo } // RegionInfo indicate the region information type RegionInfo struct { Name string Score int64 // the highest score in all clusters of the region AvailableReplicas int64 // Zones under this provider Zones map[string]struct{} // Clusters under this region, sorted by cluster.Score descending. Clusters []ClusterDetailInfo } // ZoneInfo indicate the zone information type ZoneInfo struct { Name string Score int64 // the highest score in all clusters of the zone AvailableReplicas int64 // Clusters under this zone, sorted by cluster.Score descending. Clusters []ClusterDetailInfo } // ClusterDetailInfo indicate the cluster information type ClusterDetailInfo struct { Name string Score int64 AvailableReplicas int64 Cluster *clusterv1alpha1.Cluster } // GroupClustersWithScore groups cluster base provider/region/zone/cluster func GroupClustersWithScore( clustersScore framework.ClusterScoreList, placement *policyv1alpha1.Placement, spec *workv1alpha2.ResourceBindingSpec, calAvailableReplicasFunc func(clusters []*clusterv1alpha1.Cluster, spec *workv1alpha2.ResourceBindingSpec) []workv1alpha2.TargetCluster, ) *GroupClustersInfo { if isTopologyIgnored(placement) { return groupClustersIngoreTopology(clustersScore, spec, calAvailableReplicasFunc) } return groupClustersBasedTopology(clustersScore, spec, placement.SpreadConstraints, calAvailableReplicasFunc) } func groupClustersBasedTopology( clustersScore framework.ClusterScoreList, rbSpec *workv1alpha2.ResourceBindingSpec, spreadConstraints []policyv1alpha1.SpreadConstraint, calAvailableReplicasFunc func(clusters []*clusterv1alpha1.Cluster, spec *workv1alpha2.ResourceBindingSpec) []workv1alpha2.TargetCluster, ) *GroupClustersInfo { groupClustersInfo := &GroupClustersInfo{ Providers: make(map[string]ProviderInfo), Regions: make(map[string]RegionInfo), Zones: make(map[string]ZoneInfo), } groupClustersInfo.calAvailableReplicasFunc = calAvailableReplicasFunc groupClustersInfo.generateClustersInfo(clustersScore, rbSpec) groupClustersInfo.generateZoneInfo(spreadConstraints) groupClustersInfo.generateRegionInfo(spreadConstraints) groupClustersInfo.generateProviderInfo(spreadConstraints) return groupClustersInfo } func groupClustersIngoreTopology( clustersScore framework.ClusterScoreList, rbSpec *workv1alpha2.ResourceBindingSpec, calAvailableReplicasFunc func(clusters []*clusterv1alpha1.Cluster, spec *workv1alpha2.ResourceBindingSpec) []workv1alpha2.TargetCluster, ) *GroupClustersInfo { groupClustersInfo := &GroupClustersInfo{} groupClustersInfo.calAvailableReplicasFunc = calAvailableReplicasFunc groupClustersInfo.generateClustersInfo(clustersScore, rbSpec) return groupClustersInfo } func (info *GroupClustersInfo) generateClustersInfo(clustersScore framework.ClusterScoreList, rbSpec *workv1alpha2.ResourceBindingSpec) { var clusters []*clusterv1alpha1.Cluster for _, clusterScore := range clustersScore { clusterInfo := ClusterDetailInfo{} clusterInfo.Name = clusterScore.Cluster.Name clusterInfo.Score = clusterScore.Score clusterInfo.Cluster = clusterScore.Cluster info.Clusters = append(info.Clusters, clusterInfo) clusters = append(clusters, clusterScore.Cluster) } clustersReplicas := info.calAvailableReplicasFunc(clusters, rbSpec) for i, clustersReplica := range clustersReplicas { info.Clusters[i].AvailableReplicas = int64(clustersReplica.Replicas) info.Clusters[i].AvailableReplicas += int64(rbSpec.AssignedReplicasForCluster(clustersReplica.Name)) } sortClusters(info.Clusters) } func (info *GroupClustersInfo) generateZoneInfo(spreadConstraints []policyv1alpha1.SpreadConstraint) { if !IsSpreadConstraintExisted(spreadConstraints, policyv1alpha1.SpreadByFieldZone) { return } for _, clusterInfo := range info.Clusters { zones := clusterInfo.Cluster.Spec.Zones if len(zones) == 0 { continue } for _, zone := range zones { zoneInfo, ok := info.Zones[zone] if !ok { zoneInfo = ZoneInfo{ Name: zone, Clusters: make([]ClusterDetailInfo, 0), } } zoneInfo.Clusters = append(zoneInfo.Clusters, clusterInfo) zoneInfo.AvailableReplicas += clusterInfo.AvailableReplicas info.Zones[zone] = zoneInfo } } for zone, zoneInfo := range info.Zones { zoneInfo.Score = zoneInfo.Clusters[0].Score info.Zones[zone] = zoneInfo } } func (info *GroupClustersInfo) generateRegionInfo(spreadConstraints []policyv1alpha1.SpreadConstraint) { if !IsSpreadConstraintExisted(spreadConstraints, policyv1alpha1.SpreadByFieldRegion) { return } for _, clusterInfo := range info.Clusters { region := clusterInfo.Cluster.Spec.Region if region == "" { continue } regionInfo, ok := info.Regions[region] if !ok { regionInfo = RegionInfo{ Name: region, Zones: make(map[string]struct{}), Clusters: make([]ClusterDetailInfo, 0), } } if clusterInfo.Cluster.Spec.Zone != "" { regionInfo.Zones[clusterInfo.Cluster.Spec.Zone] = struct{}{} } regionInfo.Clusters = append(regionInfo.Clusters, clusterInfo) regionInfo.AvailableReplicas += clusterInfo.AvailableReplicas info.Regions[region] = regionInfo } for region, regionInfo := range info.Regions { regionInfo.Score = regionInfo.Clusters[0].Score info.Regions[region] = regionInfo } } func (info *GroupClustersInfo) generateProviderInfo(spreadConstraints []policyv1alpha1.SpreadConstraint) { if !IsSpreadConstraintExisted(spreadConstraints, policyv1alpha1.SpreadByFieldProvider) { return } for _, clusterInfo := range info.Clusters { provider := clusterInfo.Cluster.Spec.Provider if provider == "" { continue } providerInfo, ok := info.Providers[provider] if !ok { providerInfo = ProviderInfo{ Name: provider, Regions: make(map[string]struct{}), Zones: make(map[string]struct{}), Clusters: make([]ClusterDetailInfo, 0), } } if clusterInfo.Cluster.Spec.Zone != "" { providerInfo.Zones[clusterInfo.Cluster.Spec.Zone] = struct{}{} } if clusterInfo.Cluster.Spec.Region != "" { providerInfo.Regions[clusterInfo.Cluster.Spec.Region] = struct{}{} } providerInfo.Clusters = append(providerInfo.Clusters, clusterInfo) providerInfo.AvailableReplicas += clusterInfo.AvailableReplicas info.Providers[provider] = providerInfo } for provider, providerInfo := range info.Providers { providerInfo.Score = providerInfo.Clusters[0].Score info.Providers[provider] = providerInfo } } func isTopologyIgnored(placement *policyv1alpha1.Placement) bool { spreadConstraints := placement.SpreadConstraints if len(spreadConstraints) == 0 || (len(spreadConstraints) == 1 && spreadConstraints[0].SpreadByField == policyv1alpha1.SpreadByFieldCluster) { return true } return shouldIgnoreSpreadConstraint(placement) }