karmada/pkg/descheduler/core/helper.go

127 lines
4.3 KiB
Go

package core
import (
"context"
"encoding/json"
"fmt"
"math"
"time"
"github.com/kr/pretty"
"k8s.io/klog/v2"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
estimatorclient "github.com/karmada-io/karmada/pkg/estimator/client"
"github.com/karmada-io/karmada/pkg/util"
)
// SchedulingResultHelper is a helper to wrap the ResourceBinding and its target cluster result.
type SchedulingResultHelper struct {
*workv1alpha2.ResourceBinding
TargetClusters []*TargetClusterWrapper
}
// NewSchedulingResultHelper returns a new SchedulingResultHelper based on ResourceBinding.
func NewSchedulingResultHelper(binding *workv1alpha2.ResourceBinding) *SchedulingResultHelper {
h := &SchedulingResultHelper{ResourceBinding: binding}
readyReplicas := getReadyReplicas(binding)
for i := range binding.Spec.Clusters {
targetCluster := &binding.Spec.Clusters[i]
targetClusterHelper := &TargetClusterWrapper{
ClusterName: targetCluster.Name,
Spec: targetCluster.Replicas,
}
if ready, exist := readyReplicas[targetCluster.Name]; exist {
targetClusterHelper.Ready = ready
} else {
targetClusterHelper.Ready = estimatorclient.UnauthenticReplica
}
h.TargetClusters = append(h.TargetClusters, targetClusterHelper)
}
return h
}
// FillUnschedulableReplicas will detect the unschedulable replicas of member cluster by calling
// unschedulable replica estimators and fill the unschedulable field of TargetClusterWrapper.
func (h *SchedulingResultHelper) FillUnschedulableReplicas(unschedulableThreshold time.Duration) {
reference := &h.Spec.Resource
undesiredClusters, undesiredClusterNames := h.GetUndesiredClusters()
// Set the boundary.
for i := range undesiredClusters {
undesiredClusters[i].Unschedulable = math.MaxInt32
}
// Get the minimum value of MaxAvailableReplicas in terms of all estimators.
estimators := estimatorclient.GetUnschedulableReplicaEstimators()
ctx := context.WithValue(context.TODO(), util.ContextKeyObject,
fmt.Sprintf("kind=%s, name=%s/%s", reference.Kind, reference.Namespace, reference.Name))
for _, estimator := range estimators {
res, err := estimator.GetUnschedulableReplicas(ctx, undesiredClusterNames, reference, unschedulableThreshold)
if err != nil {
klog.Errorf("Max cluster unschedulable replicas error: %v", err)
continue
}
for i := range res {
if res[i].Replicas == estimatorclient.UnauthenticReplica {
continue
}
if undesiredClusters[i].ClusterName == res[i].Name && undesiredClusters[i].Unschedulable > res[i].Replicas {
undesiredClusters[i].Unschedulable = res[i].Replicas
}
}
}
for i := range undesiredClusters {
if undesiredClusters[i].Unschedulable == math.MaxInt32 {
undesiredClusters[i].Unschedulable = 0
}
}
klog.V(4).Infof("Target undesired cluster of unschedulable replica result: %s", pretty.Sprint(undesiredClusters))
}
// GetUndesiredClusters returns the cluster which of ready replicas are not reach the ready ones.
func (h *SchedulingResultHelper) GetUndesiredClusters() ([]*TargetClusterWrapper, []string) {
var clusters []*TargetClusterWrapper
var names []string
for _, cluster := range h.TargetClusters {
if cluster.Ready < cluster.Spec {
clusters = append(clusters, cluster)
names = append(names, cluster.ClusterName)
}
}
return clusters, names
}
// TargetClusterWrapper is a wrapper to wrap the target cluster name, spec replicas,
// ready replicas and unschedulable replicas.
type TargetClusterWrapper struct {
ClusterName string
Spec int32
Ready int32
Unschedulable int32
}
func getReadyReplicas(binding *workv1alpha2.ResourceBinding) map[string]int32 {
aggregatedStatus := binding.Status.AggregatedStatus
res := make(map[string]int32, len(aggregatedStatus))
for i := range aggregatedStatus {
item := aggregatedStatus[i]
if item.Status == nil {
continue
}
workloadStatus := make(map[string]interface{})
if err := json.Unmarshal(item.Status.Raw, &workloadStatus); err != nil {
klog.ErrorS(err, "Failed to unmarshal workload status when get ready replicas", "ResourceBinding", klog.KObj(binding))
continue
}
readyReplicas := int32(0)
// TODO(Garrybest): cooperate with custom resource interpreter
if r, ok := workloadStatus[util.ReadyReplicasField]; ok {
readyReplicas = int32(r.(float64))
res[item.ClusterName] = readyReplicas
}
}
return res
}