...

Source file src/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/requested_to_capacity_ratio_test.go

Documentation: k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources

     1  /*
     2  Copyright 2019 The Kubernetes 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 noderesources
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/google/go-cmp/cmp"
    25  	"github.com/stretchr/testify/assert"
    26  	v1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/util/validation/field"
    28  	"k8s.io/klog/v2/ktesting"
    29  	"k8s.io/kubernetes/pkg/scheduler/apis/config"
    30  	"k8s.io/kubernetes/pkg/scheduler/framework"
    31  	plfeature "k8s.io/kubernetes/pkg/scheduler/framework/plugins/feature"
    32  	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
    33  	"k8s.io/kubernetes/pkg/scheduler/framework/runtime"
    34  	"k8s.io/kubernetes/pkg/scheduler/internal/cache"
    35  	st "k8s.io/kubernetes/pkg/scheduler/testing"
    36  	tf "k8s.io/kubernetes/pkg/scheduler/testing/framework"
    37  )
    38  
    39  func TestRequestedToCapacityRatioScoringStrategy(t *testing.T) {
    40  	shape := []config.UtilizationShapePoint{
    41  		{Utilization: 0, Score: 10},
    42  		{Utilization: 100, Score: 0},
    43  	}
    44  
    45  	tests := []struct {
    46  		name           string
    47  		requestedPod   *v1.Pod
    48  		nodes          []*v1.Node
    49  		existingPods   []*v1.Pod
    50  		expectedScores framework.NodeScoreList
    51  		resources      []config.ResourceSpec
    52  		shape          []config.UtilizationShapePoint
    53  		wantErrs       field.ErrorList
    54  	}{
    55  		{
    56  			name:         "nothing scheduled, nothing requested (default - least requested nodes have priority)",
    57  			requestedPod: st.MakePod().Obj(),
    58  			nodes: []*v1.Node{
    59  				st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
    60  				st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
    61  			},
    62  			existingPods: []*v1.Pod{
    63  				st.MakePod().Node("node1").Obj(),
    64  				st.MakePod().Node("node1").Obj(),
    65  			},
    66  			expectedScores: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}},
    67  			resources:      defaultResources,
    68  			shape:          shape,
    69  		},
    70  		{
    71  			name: "nothing scheduled, resources requested, differently sized nodes (default - least requested nodes have priority)",
    72  			requestedPod: st.MakePod().
    73  				Req(map[v1.ResourceName]string{"cpu": "1000", "memory": "2000"}).
    74  				Req(map[v1.ResourceName]string{"cpu": "2000", "memory": "3000"}).
    75  				Obj(),
    76  			nodes: []*v1.Node{
    77  				st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
    78  				st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
    79  			},
    80  			existingPods: []*v1.Pod{
    81  				st.MakePod().Node("node1").Obj(),
    82  				st.MakePod().Node("node1").Obj(),
    83  			},
    84  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 38}, {Name: "node2", Score: 50}},
    85  			resources:      defaultResources,
    86  			shape:          shape,
    87  		},
    88  		{
    89  			name:         "no resources requested, pods scheduled with resources (default - least requested nodes have priority)",
    90  			requestedPod: st.MakePod().Obj(),
    91  			nodes: []*v1.Node{
    92  				st.MakeNode().Name("node1").Capacity(map[v1.ResourceName]string{"cpu": "4000", "memory": "10000"}).Obj(),
    93  				st.MakeNode().Name("node2").Capacity(map[v1.ResourceName]string{"cpu": "6000", "memory": "10000"}).Obj(),
    94  			},
    95  			existingPods: []*v1.Pod{
    96  				st.MakePod().Node("node1").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000"}).Obj(),
    97  				st.MakePod().Node("node2").Req(map[v1.ResourceName]string{"cpu": "3000", "memory": "5000"}).Obj(),
    98  			},
    99  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 38}, {Name: "node2", Score: 50}},
   100  			resources:      defaultResources,
   101  			shape:          shape,
   102  		},
   103  	}
   104  
   105  	for _, test := range tests {
   106  		t.Run(test.name, func(t *testing.T) {
   107  			_, ctx := ktesting.NewTestContext(t)
   108  			ctx, cancel := context.WithCancel(ctx)
   109  			defer cancel()
   110  
   111  			state := framework.NewCycleState()
   112  			snapshot := cache.NewSnapshot(test.existingPods, test.nodes)
   113  			fh, _ := runtime.NewFramework(ctx, nil, nil, runtime.WithSnapshotSharedLister(snapshot))
   114  
   115  			p, err := NewFit(ctx, &config.NodeResourcesFitArgs{
   116  				ScoringStrategy: &config.ScoringStrategy{
   117  					Type:      config.RequestedToCapacityRatio,
   118  					Resources: test.resources,
   119  					RequestedToCapacityRatio: &config.RequestedToCapacityRatioParam{
   120  						Shape: shape,
   121  					},
   122  				},
   123  			}, fh, plfeature.Features{})
   124  
   125  			if diff := cmp.Diff(test.wantErrs.ToAggregate(), err, ignoreBadValueDetail); diff != "" {
   126  				t.Fatalf("got err (-want,+got):\n%s", diff)
   127  			}
   128  			if err != nil {
   129  				return
   130  			}
   131  
   132  			var gotScores framework.NodeScoreList
   133  			for _, n := range test.nodes {
   134  				status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.requestedPod, tf.BuildNodeInfos(test.nodes))
   135  				if !status.IsSuccess() {
   136  					t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
   137  				}
   138  				score, status := p.(framework.ScorePlugin).Score(ctx, state, test.requestedPod, n.Name)
   139  				if !status.IsSuccess() {
   140  					t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
   141  				}
   142  				gotScores = append(gotScores, framework.NodeScore{Name: n.Name, Score: score})
   143  			}
   144  
   145  			if diff := cmp.Diff(test.expectedScores, gotScores); diff != "" {
   146  				t.Errorf("Unexpected nodes (-want,+got):\n%s", diff)
   147  			}
   148  		})
   149  	}
   150  }
   151  
   152  func TestBrokenLinearFunction(t *testing.T) {
   153  	type Assertion struct {
   154  		p        int64
   155  		expected int64
   156  	}
   157  	type Test struct {
   158  		points     []helper.FunctionShapePoint
   159  		assertions []Assertion
   160  	}
   161  
   162  	tests := []Test{
   163  		{
   164  			points: []helper.FunctionShapePoint{{Utilization: 10, Score: 1}, {Utilization: 90, Score: 9}},
   165  			assertions: []Assertion{
   166  				{p: -10, expected: 1},
   167  				{p: 0, expected: 1},
   168  				{p: 9, expected: 1},
   169  				{p: 10, expected: 1},
   170  				{p: 15, expected: 1},
   171  				{p: 19, expected: 1},
   172  				{p: 20, expected: 2},
   173  				{p: 89, expected: 8},
   174  				{p: 90, expected: 9},
   175  				{p: 99, expected: 9},
   176  				{p: 100, expected: 9},
   177  				{p: 110, expected: 9},
   178  			},
   179  		},
   180  		{
   181  			points: []helper.FunctionShapePoint{{Utilization: 0, Score: 2}, {Utilization: 40, Score: 10}, {Utilization: 100, Score: 0}},
   182  			assertions: []Assertion{
   183  				{p: -10, expected: 2},
   184  				{p: 0, expected: 2},
   185  				{p: 20, expected: 6},
   186  				{p: 30, expected: 8},
   187  				{p: 40, expected: 10},
   188  				{p: 70, expected: 5},
   189  				{p: 100, expected: 0},
   190  				{p: 110, expected: 0},
   191  			},
   192  		},
   193  		{
   194  			points: []helper.FunctionShapePoint{{Utilization: 0, Score: 2}, {Utilization: 40, Score: 2}, {Utilization: 100, Score: 2}},
   195  			assertions: []Assertion{
   196  				{p: -10, expected: 2},
   197  				{p: 0, expected: 2},
   198  				{p: 20, expected: 2},
   199  				{p: 30, expected: 2},
   200  				{p: 40, expected: 2},
   201  				{p: 70, expected: 2},
   202  				{p: 100, expected: 2},
   203  				{p: 110, expected: 2},
   204  			},
   205  		},
   206  	}
   207  
   208  	for i, test := range tests {
   209  		t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) {
   210  			function := helper.BuildBrokenLinearFunction(test.points)
   211  			for _, assertion := range test.assertions {
   212  				assert.InDelta(t, assertion.expected, function(assertion.p), 0.1, "points=%v, p=%d", test.points, assertion.p)
   213  			}
   214  		})
   215  	}
   216  }
   217  
   218  func TestResourceBinPackingSingleExtended(t *testing.T) {
   219  	extendedResource1 := map[string]int64{
   220  		"intel.com/foo": 4,
   221  	}
   222  	extendedResource2 := map[string]int64{
   223  		"intel.com/foo": 8,
   224  	}
   225  	extendedResource3 := map[v1.ResourceName]string{
   226  		"intel.com/foo": "2",
   227  	}
   228  	extendedResource4 := map[v1.ResourceName]string{
   229  		"intel.com/foo": "4",
   230  	}
   231  
   232  	tests := []struct {
   233  		pod            *v1.Pod
   234  		pods           []*v1.Pod
   235  		nodes          []*v1.Node
   236  		expectedScores framework.NodeScoreList
   237  		name           string
   238  	}{
   239  		{
   240  			//  Node1 Score = Node2 Score = 0 as the incoming Pod doesn't request extended resource.
   241  			pod:            st.MakePod().Obj(),
   242  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResource2), makeNode("node2", 4000, 10000*1024*1024, extendedResource1)},
   243  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: 0}},
   244  			name:           "nothing scheduled, nothing requested",
   245  		},
   246  		{
   247  			// Node1 scores (used resources) on 0-MaxNodeScore scale
   248  			// Node1 Score:
   249  			// rawScoringFunction(used + requested / available)
   250  			// resourceScoringFunction((0+2),8)
   251  			//  = 2/8 * maxUtilization = 25 = rawScoringFunction(25)
   252  			// Node1 Score: 2
   253  			// Node2 scores (used resources) on 0-MaxNodeScore scale
   254  			// rawScoringFunction(used + requested / available)
   255  			// resourceScoringFunction((0+2),4)
   256  			//  = 2/4 * maxUtilization = 50 = rawScoringFunction(50)
   257  			// Node2 Score: 5
   258  			pod:            st.MakePod().Req(extendedResource3).Obj(),
   259  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResource2), makeNode("node2", 4000, 10000*1024*1024, extendedResource1)},
   260  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 2}, {Name: "node2", Score: 5}},
   261  			name:           "resources requested, pods scheduled with less resources",
   262  			pods:           []*v1.Pod{st.MakePod().Obj()},
   263  		},
   264  		{
   265  			// Node1 scores (used resources) on 0-MaxNodeScore scale
   266  			// Node1 Score:
   267  			// rawScoringFunction(used + requested / available)
   268  			// resourceScoringFunction((0+2),8)
   269  			//  = 2/8 * maxUtilization = 25 = rawScoringFunction(25)
   270  			// Node1 Score: 2
   271  			// Node2 scores (used resources) on 0-MaxNodeScore scale
   272  			// rawScoringFunction(used + requested / available)
   273  			// resourceScoringFunction((2+2),4)
   274  			//  = 4/4 * maxUtilization = maxUtilization = rawScoringFunction(maxUtilization)
   275  			// Node2 Score: 10
   276  			pod:            st.MakePod().Req(extendedResource3).Obj(),
   277  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResource2), makeNode("node2", 4000, 10000*1024*1024, extendedResource1)},
   278  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 2}, {Name: "node2", Score: 10}},
   279  			name:           "resources requested, pods scheduled with resources, on node with existing pod running ",
   280  			pods:           []*v1.Pod{st.MakePod().Req(extendedResource3).Node("node2").Obj()},
   281  		},
   282  		{
   283  			// Node1 scores (used resources) on 0-MaxNodeScore scale
   284  			// Node1 Score:
   285  			// rawScoringFunction(used + requested / available)
   286  			// resourceScoringFunction((0+4),8)
   287  			//  = 4/8 * maxUtilization = 50 = rawScoringFunction(50)
   288  			// Node1 Score: 5
   289  			// Node2 scores (used resources) on 0-MaxNodeScore scale
   290  			// rawScoringFunction(used + requested / available)
   291  			// resourceScoringFunction((0+4),4)
   292  			//  = 4/4 * maxUtilization = maxUtilization = rawScoringFunction(maxUtilization)
   293  			// Node2 Score: 10
   294  			pod:            st.MakePod().Req(extendedResource4).Obj(),
   295  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResource2), makeNode("node2", 4000, 10000*1024*1024, extendedResource1)},
   296  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 5}, {Name: "node2", Score: 10}},
   297  			name:           "resources requested, pods scheduled with more resources",
   298  			pods: []*v1.Pod{
   299  				st.MakePod().Obj(),
   300  			},
   301  		},
   302  	}
   303  
   304  	for _, test := range tests {
   305  		t.Run(test.name, func(t *testing.T) {
   306  			state := framework.NewCycleState()
   307  			snapshot := cache.NewSnapshot(test.pods, test.nodes)
   308  			_, ctx := ktesting.NewTestContext(t)
   309  			fh, _ := runtime.NewFramework(ctx, nil, nil, runtime.WithSnapshotSharedLister(snapshot))
   310  			args := config.NodeResourcesFitArgs{
   311  				ScoringStrategy: &config.ScoringStrategy{
   312  					Type: config.RequestedToCapacityRatio,
   313  					Resources: []config.ResourceSpec{
   314  						{Name: "intel.com/foo", Weight: 1},
   315  					},
   316  					RequestedToCapacityRatio: &config.RequestedToCapacityRatioParam{
   317  						Shape: []config.UtilizationShapePoint{
   318  							{Utilization: 0, Score: 0},
   319  							{Utilization: 100, Score: 1},
   320  						},
   321  					},
   322  				},
   323  			}
   324  			p, err := NewFit(ctx, &args, fh, plfeature.Features{})
   325  			if err != nil {
   326  				t.Fatalf("unexpected error: %v", err)
   327  			}
   328  
   329  			var gotList framework.NodeScoreList
   330  			for _, n := range test.nodes {
   331  				status := p.(framework.PreScorePlugin).PreScore(context.Background(), state, test.pod, tf.BuildNodeInfos(test.nodes))
   332  				if !status.IsSuccess() {
   333  					t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
   334  				}
   335  				score, status := p.(framework.ScorePlugin).Score(context.Background(), state, test.pod, n.Name)
   336  				if !status.IsSuccess() {
   337  					t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
   338  				}
   339  				gotList = append(gotList, framework.NodeScore{Name: n.Name, Score: score})
   340  			}
   341  
   342  			if diff := cmp.Diff(test.expectedScores, gotList); diff != "" {
   343  				t.Errorf("Unexpected nodescore list (-want,+got):\n%s", diff)
   344  			}
   345  		})
   346  	}
   347  }
   348  
   349  func TestResourceBinPackingMultipleExtended(t *testing.T) {
   350  	extendedResources1 := map[string]int64{
   351  		"intel.com/foo": 4,
   352  		"intel.com/bar": 8,
   353  	}
   354  	extendedResources2 := map[string]int64{
   355  		"intel.com/foo": 8,
   356  		"intel.com/bar": 4,
   357  	}
   358  
   359  	extendedResourcePod1 := map[v1.ResourceName]string{
   360  		"intel.com/foo": "2",
   361  		"intel.com/bar": "2",
   362  	}
   363  	extendedResourcePod2 := map[v1.ResourceName]string{
   364  		"intel.com/foo": "4",
   365  		"intel.com/bar": "2",
   366  	}
   367  
   368  	tests := []struct {
   369  		pod            *v1.Pod
   370  		pods           []*v1.Pod
   371  		nodes          []*v1.Node
   372  		expectedScores framework.NodeScoreList
   373  		name           string
   374  	}{
   375  		{
   376  
   377  			// resources["intel.com/foo"] = 3
   378  			// resources["intel.com/bar"] = 5
   379  			// Node1 scores (used resources) on 0-10 scale
   380  			// Node1 Score:
   381  			// intel.com/foo:
   382  			// rawScoringFunction(used + requested / available)
   383  			// resourceScoringFunction((0+0),8)
   384  			//  = 0/8 * 100 = 0 = rawScoringFunction(0)
   385  			// intel.com/bar:
   386  			// rawScoringFunction(used + requested / available)
   387  			// resourceScoringFunction((0+0),4)
   388  			//  = 0/4 * 100 = 0 = rawScoringFunction(0)
   389  			// Node1 Score: (0 * 3) + (0 * 5) / 8 = 0
   390  
   391  			// Node2 scores (used resources) on 0-10 scale
   392  			// rawScoringFunction(used + requested / available)
   393  			// intel.com/foo:
   394  			// rawScoringFunction(used + requested / available)
   395  			// resourceScoringFunction((0+0),4)
   396  			//  = 0/4 * 100 = 0 = rawScoringFunction(0)
   397  			// intel.com/bar:
   398  			// rawScoringFunction(used + requested / available)
   399  			// resourceScoringFunction((0+0),8)
   400  			//  = 0/8 * 100 = 0 = rawScoringFunction(0)
   401  			// Node2 Score: (0 * 3) + (0 * 5) / 8 = 0
   402  
   403  			pod:            st.MakePod().Obj(),
   404  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResources2), makeNode("node2", 4000, 10000*1024*1024, extendedResources1)},
   405  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: 0}},
   406  			name:           "nothing scheduled, nothing requested",
   407  		},
   408  		{
   409  
   410  			// resources["intel.com/foo"] = 3
   411  			// resources["intel.com/bar"] = 5
   412  			// Node1 scores (used resources) on 0-10 scale
   413  			// Node1 Score:
   414  			// intel.com/foo:
   415  			// rawScoringFunction(used + requested / available)
   416  			// resourceScoringFunction((0+2),8)
   417  			//  = 2/8 * 100 = 25 = rawScoringFunction(25)
   418  			// intel.com/bar:
   419  			// rawScoringFunction(used + requested / available)
   420  			// resourceScoringFunction((0+2),4)
   421  			//  = 2/4 * 100 = 50 = rawScoringFunction(50)
   422  			// Node1 Score: (2 * 3) + (5 * 5) / 8 = 4
   423  
   424  			// Node2 scores (used resources) on 0-10 scale
   425  			// rawScoringFunction(used + requested / available)
   426  			// intel.com/foo:
   427  			// rawScoringFunction(used + requested / available)
   428  			// resourceScoringFunction((0+2),4)
   429  			//  = 2/4 * 100 = 50 = rawScoringFunction(50)
   430  			// intel.com/bar:
   431  			// rawScoringFunction(used + requested / available)
   432  			// resourceScoringFunction((0+2),8)
   433  			//  = 2/8 * 100 = 25 = rawScoringFunction(25)
   434  			// Node2 Score: (5 * 3) + (2 * 5) / 8 = 3
   435  
   436  			pod:            st.MakePod().Req(extendedResourcePod1).Obj(),
   437  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResources2), makeNode("node2", 4000, 10000*1024*1024, extendedResources1)},
   438  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 4}, {Name: "node2", Score: 3}},
   439  			name:           "resources requested, pods scheduled with less resources",
   440  			pods: []*v1.Pod{
   441  				st.MakePod().Obj(),
   442  			},
   443  		},
   444  		{
   445  
   446  			// resources["intel.com/foo"] = 3
   447  			// resources["intel.com/bar"] = 5
   448  			// Node1 scores (used resources) on 0-10 scale
   449  			// Node1 Score:
   450  			// intel.com/foo:
   451  			// rawScoringFunction(used + requested / available)
   452  			// resourceScoringFunction((0+2),8)
   453  			//  = 2/8 * 100 = 25 = rawScoringFunction(25)
   454  			// intel.com/bar:
   455  			// rawScoringFunction(used + requested / available)
   456  			// resourceScoringFunction((0+2),4)
   457  			//  = 2/4 * 100 = 50 = rawScoringFunction(50)
   458  			// Node1 Score: (2 * 3) + (5 * 5) / 8 = 4
   459  			// Node2 scores (used resources) on 0-10 scale
   460  			// rawScoringFunction(used + requested / available)
   461  			// intel.com/foo:
   462  			// rawScoringFunction(used + requested / available)
   463  			// resourceScoringFunction((2+2),4)
   464  			//  = 4/4 * 100 = 100 = rawScoringFunction(100)
   465  			// intel.com/bar:
   466  			// rawScoringFunction(used + requested / available)
   467  			// resourceScoringFunction((2+2),8)
   468  			//  = 4/8 *100 = 50 = rawScoringFunction(50)
   469  			// Node2 Score: (10 * 3) + (5 * 5) / 8 = 7
   470  
   471  			pod:            st.MakePod().Req(extendedResourcePod1).Obj(),
   472  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResources2), makeNode("node2", 4000, 10000*1024*1024, extendedResources1)},
   473  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 4}, {Name: "node2", Score: 7}},
   474  			name:           "resources requested, pods scheduled with resources, on node with existing pod running ",
   475  			pods:           []*v1.Pod{st.MakePod().Req(extendedResourcePod2).Node("node2").Obj()},
   476  		},
   477  		{
   478  
   479  			// resources["intel.com/foo"] = 3
   480  			// resources["intel.com/bar"] = 5
   481  			// Node1 scores (used resources) on 0-10 scale
   482  			// used + requested / available
   483  			// intel.com/foo Score: { (0 + 4) / 8 } * 10 = 0
   484  			// intel.com/bar Score: { (0 + 2) / 4 } * 10 = 0
   485  			// Node1 Score: (0.25 * 3) + (0.5 * 5) / 8 = 5
   486  			// resources["intel.com/foo"] = 3
   487  			// resources["intel.com/bar"] = 5
   488  			// Node2 scores (used resources) on 0-10 scale
   489  			// used + requested / available
   490  			// intel.com/foo Score: { (0 + 4) / 4 } * 10 = 0
   491  			// intel.com/bar Score: { (0 + 2) / 8 } * 10 = 0
   492  			// Node2 Score: (1 * 3) + (0.25 * 5) / 8 = 5
   493  
   494  			// resources["intel.com/foo"] = 3
   495  			// resources["intel.com/bar"] = 5
   496  			// Node1 scores (used resources) on 0-10 scale
   497  			// Node1 Score:
   498  			// intel.com/foo:
   499  			// rawScoringFunction(used + requested / available)
   500  			// resourceScoringFunction((0+4),8)
   501  			// 4/8 * 100 = 50 = rawScoringFunction(50)
   502  			// intel.com/bar:
   503  			// rawScoringFunction(used + requested / available)
   504  			// resourceScoringFunction((0+2),4)
   505  			//  = 2/4 * 100 = 50 = rawScoringFunction(50)
   506  			// Node1 Score: (5 * 3) + (5 * 5) / 8 = 5
   507  			// Node2 scores (used resources) on 0-10 scale
   508  			// rawScoringFunction(used + requested / available)
   509  			// intel.com/foo:
   510  			// rawScoringFunction(used + requested / available)
   511  			// resourceScoringFunction((0+4),4)
   512  			//  = 4/4 * 100 = 100 = rawScoringFunction(100)
   513  			// intel.com/bar:
   514  			// rawScoringFunction(used + requested / available)
   515  			// resourceScoringFunction((0+2),8)
   516  			//  = 2/8 * 100 = 25 = rawScoringFunction(25)
   517  			// Node2 Score: (10 * 3) + (2 * 5) / 8 = 5
   518  
   519  			pod:            st.MakePod().Req(extendedResourcePod2).Obj(),
   520  			nodes:          []*v1.Node{makeNode("node1", 4000, 10000*1024*1024, extendedResources2), makeNode("node2", 4000, 10000*1024*1024, extendedResources1)},
   521  			expectedScores: []framework.NodeScore{{Name: "node1", Score: 5}, {Name: "node2", Score: 5}},
   522  			name:           "resources requested, pods scheduled with more resources",
   523  			pods: []*v1.Pod{
   524  				st.MakePod().Obj(),
   525  			},
   526  		},
   527  	}
   528  
   529  	for _, test := range tests {
   530  		t.Run(test.name, func(t *testing.T) {
   531  			state := framework.NewCycleState()
   532  			snapshot := cache.NewSnapshot(test.pods, test.nodes)
   533  			_, ctx := ktesting.NewTestContext(t)
   534  			fh, _ := runtime.NewFramework(ctx, nil, nil, runtime.WithSnapshotSharedLister(snapshot))
   535  
   536  			args := config.NodeResourcesFitArgs{
   537  				ScoringStrategy: &config.ScoringStrategy{
   538  					Type: config.RequestedToCapacityRatio,
   539  					Resources: []config.ResourceSpec{
   540  						{Name: "intel.com/foo", Weight: 3},
   541  						{Name: "intel.com/bar", Weight: 5},
   542  					},
   543  					RequestedToCapacityRatio: &config.RequestedToCapacityRatioParam{
   544  						Shape: []config.UtilizationShapePoint{
   545  							{Utilization: 0, Score: 0},
   546  							{Utilization: 100, Score: 1},
   547  						},
   548  					},
   549  				},
   550  			}
   551  
   552  			p, err := NewFit(ctx, &args, fh, plfeature.Features{})
   553  			if err != nil {
   554  				t.Fatalf("unexpected error: %v", err)
   555  			}
   556  
   557  			status := p.(framework.PreScorePlugin).PreScore(context.Background(), state, test.pod, tf.BuildNodeInfos(test.nodes))
   558  			if !status.IsSuccess() {
   559  				t.Errorf("PreScore is expected to return success, but didn't. Got status: %v", status)
   560  			}
   561  
   562  			var gotScores framework.NodeScoreList
   563  			for _, n := range test.nodes {
   564  				score, status := p.(framework.ScorePlugin).Score(context.Background(), state, test.pod, n.Name)
   565  				if !status.IsSuccess() {
   566  					t.Errorf("Score is expected to return success, but didn't. Got status: %v", status)
   567  				}
   568  				gotScores = append(gotScores, framework.NodeScore{Name: n.Name, Score: score})
   569  			}
   570  
   571  			if diff := cmp.Diff(test.expectedScores, gotScores); diff != "" {
   572  				t.Errorf("Unexpected nodescore list (-want,+got):\n%s", diff)
   573  			}
   574  		})
   575  	}
   576  }
   577  

View as plain text