github.com/kubewharf/katalyst-core@v0.5.3/pkg/scheduler/plugins/qosawarenoderesources/fit_test.go (about)

     1  /*
     2  Copyright 2022 The Katalyst Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package qosawarenoderesources
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  	v1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	kubeschedulerconfig "k8s.io/kubernetes/pkg/scheduler/apis/config"
    30  	"k8s.io/kubernetes/pkg/scheduler/framework"
    31  
    32  	apis "github.com/kubewharf/katalyst-api/pkg/apis/node/v1alpha1"
    33  	"github.com/kubewharf/katalyst-api/pkg/apis/scheduling/config"
    34  	"github.com/kubewharf/katalyst-api/pkg/consts"
    35  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    36  	"github.com/kubewharf/katalyst-core/pkg/scheduler/cache"
    37  	"github.com/kubewharf/katalyst-core/pkg/scheduler/util"
    38  	"github.com/kubewharf/katalyst-core/pkg/util/native"
    39  )
    40  
    41  var makeFit = func(scoringStrategyType kubeschedulerconfig.ScoringStrategyType) (framework.Plugin, error) {
    42  	return NewFit(&config.QoSAwareNodeResourcesFitArgs{
    43  		ScoringStrategy: &config.ScoringStrategy{
    44  			Type: scoringStrategyType,
    45  			Resources: []kubeschedulerconfig.ResourceSpec{
    46  				{
    47  					Name:   fmt.Sprintf("%s", v1.ResourceCPU),
    48  					Weight: 30,
    49  				},
    50  				{
    51  					Name:   fmt.Sprintf("%s", v1.ResourceMemory),
    52  					Weight: 70,
    53  				},
    54  			},
    55  			ReclaimedResources: []kubeschedulerconfig.ResourceSpec{
    56  				{
    57  					Name:   fmt.Sprintf("%s", consts.ReclaimedResourceMilliCPU),
    58  					Weight: 30,
    59  				},
    60  				{
    61  					Name:   fmt.Sprintf("%s", consts.ReclaimedResourceMemory),
    62  					Weight: 70,
    63  				},
    64  			},
    65  			RequestedToCapacityRatio: &kubeschedulerconfig.RequestedToCapacityRatioParam{
    66  				Shape: []kubeschedulerconfig.UtilizationShapePoint{
    67  					{
    68  						Utilization: 10,
    69  						Score:       3,
    70  					},
    71  					{
    72  						Utilization: 20,
    73  						Score:       4,
    74  					},
    75  					{
    76  						Utilization: 50,
    77  						Score:       7,
    78  					},
    79  				},
    80  			},
    81  			ReclaimedRequestedToCapacityRatio: &kubeschedulerconfig.RequestedToCapacityRatioParam{
    82  				Shape: []kubeschedulerconfig.UtilizationShapePoint{
    83  					{
    84  						Utilization: 10,
    85  						Score:       3,
    86  					},
    87  					{
    88  						Utilization: 20,
    89  						Score:       4,
    90  					},
    91  					{
    92  						Utilization: 50,
    93  						Score:       7,
    94  					},
    95  				},
    96  			},
    97  		},
    98  	}, nil)
    99  }
   100  
   101  var makeFitPod = func(uid types.UID, name string, res v1.ResourceList, node string) *v1.Pod {
   102  	pod := &v1.Pod{
   103  		ObjectMeta: metav1.ObjectMeta{
   104  			Namespace: "n1",
   105  			Name:      name,
   106  			UID:       uid,
   107  			Annotations: map[string]string{
   108  				consts.PodAnnotationQoSLevelKey: consts.PodAnnotationQoSLevelReclaimedCores,
   109  			},
   110  		},
   111  		Spec: v1.PodSpec{
   112  			Containers: []v1.Container{
   113  				{
   114  					Name: "c1",
   115  					Resources: v1.ResourceRequirements{
   116  						Limits:   res,
   117  						Requests: res,
   118  					},
   119  				},
   120  			},
   121  			NodeName: node,
   122  		},
   123  	}
   124  	return pod
   125  }
   126  
   127  var makeFitNode = func(name string, pods []*v1.Pod, res v1.ResourceList) *framework.NodeInfo {
   128  	ni := framework.NewNodeInfo(pods...)
   129  	ni.SetNode(&v1.Node{
   130  		ObjectMeta: metav1.ObjectMeta{
   131  			Name: name,
   132  		},
   133  		Status: v1.NodeStatus{
   134  			Allocatable: res,
   135  		},
   136  	})
   137  	return ni
   138  }
   139  
   140  var makeFitCNR = func(name string, res v1.ResourceList) *apis.CustomNodeResource {
   141  	cnr := &apis.CustomNodeResource{
   142  		ObjectMeta: metav1.ObjectMeta{Name: name},
   143  		Status: apis.CustomNodeResourceStatus{
   144  			Resources: apis.Resources{
   145  				Allocatable: &res,
   146  			},
   147  		},
   148  	}
   149  	return cnr
   150  }
   151  
   152  func Test_Fit(t *testing.T) {
   153  	util.SetQoSConfig(generic.NewQoSConfiguration())
   154  
   155  	f, err := makeFit(kubeschedulerconfig.LeastAllocated)
   156  	assert.Nil(t, err)
   157  	fit := f.(*Fit)
   158  
   159  	state := framework.NewCycleState()
   160  	p1 := makeFitPod("p1", "p1", map[v1.ResourceName]resource.Quantity{
   161  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(2000, resource.DecimalSI),
   162  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(2*1024*0o124*1024, resource.DecimalSI),
   163  	}, "c1")
   164  
   165  	fit.PreFilter(context.Background(), state, p1)
   166  	data, err := state.Read(preFilterStateKey)
   167  	assert.Nil(t, err)
   168  	assert.Equal(t, data, &preFilterState{
   169  		QoSResource: native.QoSResource{
   170  			ReclaimedMilliCPU: int64(2000),
   171  			ReclaimedMemory:   int64(2 * 1024 * 0o124 * 1024),
   172  		},
   173  	})
   174  
   175  	assert.Nil(t, fit.PreFilterExtensions())
   176  
   177  	e := []framework.ClusterEvent{
   178  		{Resource: framework.Pod, ActionType: framework.Delete},
   179  		{Resource: framework.Node, ActionType: framework.Add},
   180  	}
   181  	assert.Equal(t, fit.EventsToRegister(), e)
   182  	assert.Nil(t, fit.ScoreExtensions())
   183  
   184  	fit.Reserve(context.Background(), nil, p1, "c1")
   185  	node, err := cache.GetCache().GetNodeInfo("c1")
   186  	assert.Nil(t, err)
   187  	assert.Equal(t, node.QoSResourcesAllocatable.ReclaimedMilliCPU, int64(0))
   188  	assert.Equal(t, node.QoSResourcesAllocatable.ReclaimedMemory, int64(0))
   189  	assert.Equal(t, node.QoSResourcesRequested.ReclaimedMilliCPU, int64(2000))
   190  	assert.Equal(t, node.QoSResourcesRequested.ReclaimedMemory, int64(2*1024*0o124*1024))
   191  
   192  	fit.Unreserve(context.Background(), nil, p1, "c1")
   193  	node, err = cache.GetCache().GetNodeInfo("c1")
   194  	assert.Nil(t, err)
   195  	assert.Equal(t, node.QoSResourcesAllocatable.ReclaimedMilliCPU, int64(0))
   196  	assert.Equal(t, node.QoSResourcesAllocatable.ReclaimedMemory, int64(0))
   197  	assert.Equal(t, node.QoSResourcesRequested.ReclaimedMilliCPU, int64(0))
   198  	assert.Equal(t, node.QoSResourcesRequested.ReclaimedMemory, int64(0))
   199  }
   200  
   201  func Test_Allocated(t *testing.T) {
   202  	util.SetQoSConfig(generic.NewQoSConfiguration())
   203  
   204  	f, _ := makeFit(kubeschedulerconfig.LeastAllocated)
   205  	fit := f.(*Fit)
   206  
   207  	state := framework.NewCycleState()
   208  	p1 := makeFitPod("p1", "p1", map[v1.ResourceName]resource.Quantity{
   209  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(2000, resource.DecimalSI),
   210  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(2*1024*0o124*1024, resource.DecimalSI),
   211  	}, "n1")
   212  	p2 := makeFitPod("p2", "p2", map[v1.ResourceName]resource.Quantity{
   213  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(3000, resource.DecimalSI),
   214  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(3*1024*0o124*1024, resource.DecimalSI),
   215  	}, "n1")
   216  	n1 := makeFitNode("n1", []*v1.Pod{p1, p2}, map[v1.ResourceName]resource.Quantity{
   217  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(9000, resource.DecimalSI),
   218  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(12*1024*0o124*1024, resource.DecimalSI),
   219  	})
   220  
   221  	p3 := makeFitPod("p3", "p3", map[v1.ResourceName]resource.Quantity{
   222  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(3000, resource.DecimalSI),
   223  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(3*1024*0o124*1024, resource.DecimalSI),
   224  	}, "")
   225  	fit.PreFilter(context.Background(), state, p3)
   226  
   227  	status := fit.Filter(context.Background(), state, p3, n1)
   228  	t.Logf("%v", status.Reasons())
   229  	assert.Equal(t, status.IsSuccess(), false)
   230  
   231  	c1 := makeFitCNR("n1", map[v1.ResourceName]resource.Quantity{
   232  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(9000, resource.DecimalSI),
   233  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(12*1024*0o124*1024, resource.DecimalSI),
   234  	})
   235  	cache.GetCache().AddOrUpdateCNR(c1)
   236  	status = fit.Filter(context.Background(), state, p3, n1)
   237  	assert.Equal(t, status.IsSuccess(), true)
   238  
   239  	p4 := makeFitPod("p4", "p4", map[v1.ResourceName]resource.Quantity{
   240  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(3000, resource.DecimalSI),
   241  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(30*1024*0o124*1024, resource.DecimalSI),
   242  	}, "")
   243  	fit.PreFilter(context.Background(), state, p4)
   244  
   245  	status = fit.Filter(context.Background(), state, p4, n1)
   246  	t.Logf("%v", status)
   247  	assert.Equal(t, status.IsSuccess(), false)
   248  }
   249  
   250  func Test_FitScore(t *testing.T) {
   251  	util.SetQoSConfig(generic.NewQoSConfiguration())
   252  
   253  	state := framework.NewCycleState()
   254  	p1 := makeFitPod("p1", "p1", map[v1.ResourceName]resource.Quantity{
   255  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(2000, resource.DecimalSI),
   256  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(2*1024*0o124*1024, resource.DecimalSI),
   257  	}, "n1")
   258  	_ = cache.GetCache().AddPod(p1)
   259  
   260  	p2 := makeFitPod("p2", "p2", map[v1.ResourceName]resource.Quantity{
   261  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(3000, resource.DecimalSI),
   262  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(3*1024*0o124*1024, resource.DecimalSI),
   263  	}, "n1")
   264  	_ = cache.GetCache().AddPod(p2)
   265  
   266  	c1 := makeFitCNR("n1", map[v1.ResourceName]resource.Quantity{
   267  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(9000, resource.DecimalSI),
   268  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(12*1024*0o124*1024, resource.DecimalSI),
   269  	})
   270  	cache.GetCache().AddOrUpdateCNR(c1)
   271  
   272  	p3 := makeFitPod("p3", "p3", map[v1.ResourceName]resource.Quantity{
   273  		consts.ReclaimedResourceMilliCPU: *resource.NewQuantity(3000, resource.DecimalSI),
   274  		consts.ReclaimedResourceMemory:   *resource.NewQuantity(3*1024*0o124*1024, resource.DecimalSI),
   275  	}, "")
   276  
   277  	leastF, _ := makeFit(kubeschedulerconfig.LeastAllocated)
   278  	leastFit := leastF.(*Fit)
   279  	score, _ := leastFit.Score(context.Background(), state, p3, "n1")
   280  	assert.Equal(t, score, int64(26))
   281  
   282  	mostF, _ := makeFit(kubeschedulerconfig.MostAllocated)
   283  	mostFit := mostF.(*Fit)
   284  	score, _ = mostFit.Score(context.Background(), state, p3, "n1")
   285  	assert.Equal(t, score, int64(72))
   286  
   287  	ratioF, _ := makeFit(kubeschedulerconfig.RequestedToCapacityRatio)
   288  	ratioFit := ratioF.(*Fit)
   289  	score, _ = ratioFit.Score(context.Background(), state, p3, "n1")
   290  	assert.Equal(t, score, int64(70))
   291  }