volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/nodegroup/nodegroup_test.go (about)

     1  package nodegroup
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/agiledragon/gomonkey/v2"
     9  	v1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/client-go/tools/record"
    12  
    13  	batch "volcano.sh/apis/pkg/apis/batch/v1alpha1"
    14  	schedulingv1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    15  	"volcano.sh/volcano/pkg/scheduler/api"
    16  	"volcano.sh/volcano/pkg/scheduler/cache"
    17  	"volcano.sh/volcano/pkg/scheduler/conf"
    18  	"volcano.sh/volcano/pkg/scheduler/framework"
    19  	"volcano.sh/volcano/pkg/scheduler/util"
    20  )
    21  
    22  func TestNodeGroup(t *testing.T) {
    23  	var tmp *cache.SchedulerCache
    24  	patchUpdateQueueStatus := gomonkey.ApplyMethod(reflect.TypeOf(tmp), "UpdateQueueStatus", func(scCache *cache.SchedulerCache, queue *api.QueueInfo) error {
    25  		return nil
    26  	})
    27  	defer patchUpdateQueueStatus.Reset()
    28  
    29  	framework.RegisterPluginBuilder(PluginName, New)
    30  	defer framework.CleanupPluginBuilders()
    31  
    32  	p1 := util.BuildPod("c1", "p1", "", v1.PodPending, api.BuildResourceList("2", "4Gi"), "pg1", map[string]string{
    33  		batch.QueueNameKey: "q1",
    34  	}, make(map[string]string))
    35  
    36  	p2 := util.BuildPod("c1", "p2", "", v1.PodPending, api.BuildResourceList("2", "4Gi"), "pg2", map[string]string{
    37  		batch.QueueNameKey: "q2",
    38  	}, make(map[string]string))
    39  
    40  	n1 := util.BuildNode("n1", api.BuildResourceList("2", "4Gi"), map[string]string{
    41  		NodeGroupNameKey: "group1",
    42  	})
    43  	n2 := util.BuildNode("n2", api.BuildResourceList("4", "16Gi"), map[string]string{
    44  		NodeGroupNameKey: "group2",
    45  	})
    46  	n3 := util.BuildNode("n3", api.BuildResourceList("4", "16Gi"), map[string]string{
    47  		NodeGroupNameKey: "group3",
    48  	})
    49  	n4 := util.BuildNode("n4", api.BuildResourceList("4", "16Gi"), map[string]string{
    50  		NodeGroupNameKey: "group4",
    51  	})
    52  	n5 := util.BuildNode("n5", api.BuildResourceList("4", "16Gi"), make(map[string]string))
    53  
    54  	pg1 := &schedulingv1.PodGroup{
    55  		ObjectMeta: metav1.ObjectMeta{
    56  			Name:      "pg1",
    57  			Namespace: "c1",
    58  		},
    59  		Spec: schedulingv1.PodGroupSpec{
    60  			Queue: "q1",
    61  		},
    62  	}
    63  
    64  	pg2 := &schedulingv1.PodGroup{
    65  		ObjectMeta: metav1.ObjectMeta{
    66  			Name:      "pg2",
    67  			Namespace: "c1",
    68  		},
    69  		Spec: schedulingv1.PodGroupSpec{
    70  			Queue: "q2",
    71  		},
    72  	}
    73  
    74  	queue1 := &schedulingv1.Queue{
    75  		ObjectMeta: metav1.ObjectMeta{
    76  			Name: "q1",
    77  		},
    78  		Spec: schedulingv1.QueueSpec{
    79  			Weight: 1,
    80  			Affinity: &schedulingv1.Affinity{
    81  				NodeGroupAffinity: &schedulingv1.NodeGroupAffinity{
    82  					RequiredDuringSchedulingIgnoredDuringExecution:  []string{"group1", "group3"},
    83  					PreferredDuringSchedulingIgnoredDuringExecution: []string{"group3"},
    84  				},
    85  				NodeGroupAntiAffinity: &schedulingv1.NodeGroupAntiAffinity{
    86  					RequiredDuringSchedulingIgnoredDuringExecution:  []string{"group2", "group4"},
    87  					PreferredDuringSchedulingIgnoredDuringExecution: []string{"group4"},
    88  				},
    89  			},
    90  		},
    91  	}
    92  
    93  	queue2 := &schedulingv1.Queue{
    94  		ObjectMeta: metav1.ObjectMeta{
    95  			Name: "q2",
    96  		},
    97  		Spec: schedulingv1.QueueSpec{
    98  			Weight: 1,
    99  			Affinity: &schedulingv1.Affinity{
   100  				NodeGroupAffinity: &schedulingv1.NodeGroupAffinity{
   101  					RequiredDuringSchedulingIgnoredDuringExecution:  []string{"group1"},
   102  					PreferredDuringSchedulingIgnoredDuringExecution: []string{"group3"},
   103  				},
   104  				NodeGroupAntiAffinity: &schedulingv1.NodeGroupAntiAffinity{
   105  					RequiredDuringSchedulingIgnoredDuringExecution:  []string{"group2"},
   106  					PreferredDuringSchedulingIgnoredDuringExecution: []string{"group4"},
   107  				},
   108  			},
   109  		},
   110  	}
   111  
   112  	tests := []struct {
   113  		name           string
   114  		podGroups      []*schedulingv1.PodGroup
   115  		pods           []*v1.Pod
   116  		nodes          []*v1.Node
   117  		queues         []*schedulingv1.Queue
   118  		arguments      framework.Arguments
   119  		expected       map[string]map[string]float64
   120  		expectedStatus map[string]map[string]int
   121  	}{
   122  		{
   123  			name: "case: soft constraints is subset of hard constraints",
   124  			podGroups: []*schedulingv1.PodGroup{
   125  				pg1,
   126  			},
   127  			queues: []*schedulingv1.Queue{
   128  				queue1,
   129  			},
   130  			pods: []*v1.Pod{
   131  				p1,
   132  			},
   133  			nodes: []*v1.Node{
   134  				n1, n2, n3, n4, n5,
   135  			},
   136  			arguments: framework.Arguments{},
   137  			expected: map[string]map[string]float64{
   138  				"c1/p1": {
   139  					"n1": 100,
   140  					"n2": 0.0,
   141  					"n3": 150,
   142  					"n4": -1,
   143  					"n5": 0.0,
   144  				},
   145  			},
   146  			expectedStatus: map[string]map[string]int{
   147  				"c1/p1": {
   148  					"n1": api.Success,
   149  					"n2": api.UnschedulableAndUnresolvable,
   150  					"n3": api.Success,
   151  					"n4": api.Success,
   152  					"n5": api.UnschedulableAndUnresolvable,
   153  				},
   154  			},
   155  		},
   156  		{
   157  			// test unnormal case
   158  			name: "case: soft constraints is not subset of hard constraints",
   159  			podGroups: []*schedulingv1.PodGroup{
   160  				pg2,
   161  			},
   162  			queues: []*schedulingv1.Queue{
   163  				queue2,
   164  			},
   165  			pods: []*v1.Pod{
   166  				p2,
   167  			},
   168  			nodes: []*v1.Node{
   169  				n1, n2, n3, n4, n5,
   170  			},
   171  			arguments: framework.Arguments{},
   172  			expected: map[string]map[string]float64{
   173  				"c1/p2": {
   174  					"n1": 100,
   175  					"n2": 0.0,
   176  					"n3": 50,
   177  					"n4": -1,
   178  					"n5": 0.0,
   179  				},
   180  			},
   181  			expectedStatus: map[string]map[string]int{
   182  				"c1/p2": {
   183  					"n1": api.Success,
   184  					"n2": api.UnschedulableAndUnresolvable,
   185  					"n3": api.Success,
   186  					"n4": api.Success,
   187  					"n5": api.UnschedulableAndUnresolvable,
   188  				},
   189  			},
   190  		},
   191  	}
   192  
   193  	for i, test := range tests {
   194  		t.Run(fmt.Sprintf("case %v %v", i, test.name), func(t *testing.T) {
   195  			binder := &util.FakeBinder{
   196  				Binds:   map[string]string{},
   197  				Channel: make(chan string),
   198  			}
   199  			schedulerCache := &cache.SchedulerCache{
   200  				Nodes:         make(map[string]*api.NodeInfo),
   201  				Jobs:          make(map[api.JobID]*api.JobInfo),
   202  				Queues:        make(map[api.QueueID]*api.QueueInfo),
   203  				Binder:        binder,
   204  				StatusUpdater: &util.FakeStatusUpdater{},
   205  				VolumeBinder:  &util.FakeVolumeBinder{},
   206  
   207  				Recorder: record.NewFakeRecorder(100),
   208  			}
   209  
   210  			for _, node := range test.nodes {
   211  				schedulerCache.AddOrUpdateNode(node)
   212  			}
   213  			for _, pod := range test.pods {
   214  				schedulerCache.AddPod(pod)
   215  			}
   216  			for _, ss := range test.podGroups {
   217  				schedulerCache.AddPodGroupV1beta1(ss)
   218  			}
   219  			for _, q := range test.queues {
   220  				schedulerCache.AddQueueV1beta1(q)
   221  			}
   222  
   223  			trueValue := true
   224  			ssn := framework.OpenSession(schedulerCache, []conf.Tier{
   225  				{
   226  					Plugins: []conf.PluginOption{
   227  						{
   228  							Name:             PluginName,
   229  							EnabledNodeOrder: &trueValue,
   230  							EnabledPredicate: &trueValue,
   231  							Arguments:        test.arguments,
   232  						},
   233  					},
   234  				},
   235  			}, nil)
   236  			defer framework.CloseSession(ssn)
   237  
   238  			for _, job := range ssn.Jobs {
   239  				for _, task := range job.Tasks {
   240  					taskID := fmt.Sprintf("%s/%s", task.Namespace, task.Name)
   241  
   242  					for _, node := range ssn.Nodes {
   243  						score, err := ssn.NodeOrderFn(task, node)
   244  						if err != nil {
   245  							t.Errorf("case%d: task %s on node %s has err %v", i, taskID, node.Name, err)
   246  							continue
   247  						}
   248  						if expectScore := test.expected[taskID][node.Name]; expectScore != score {
   249  							t.Errorf("case%d: task %s on node %s expect have score %v, but get %v", i, taskID, node.Name, expectScore, score)
   250  						}
   251  
   252  						status, _ := ssn.PredicateFn(task, node)
   253  						if expectStatus := test.expectedStatus[taskID][node.Name]; expectStatus != status[0].Code {
   254  							t.Errorf("case%d: task %s on node %s expect have status code %v, but get %v", i, taskID, node.Name, expectStatus, status[0].Code)
   255  						}
   256  
   257  					}
   258  				}
   259  			}
   260  			t.Logf("nodegroup unit test finished ")
   261  		})
   262  	}
   263  
   264  }