volcano.sh/volcano@v1.9.0/pkg/scheduler/plugins/task-topology/topology_test.go (about)

     1  /*
     2  Copyright 2021 The Volcano 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 tasktopology
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"testing"
    23  
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  
    26  	"volcano.sh/apis/pkg/apis/scheduling"
    27  	"volcano.sh/volcano/pkg/scheduler/api"
    28  )
    29  
    30  func Test_readTopologyFromPgAnnotations(t *testing.T) {
    31  	cases := []struct {
    32  		description string
    33  		job         *api.JobInfo
    34  		topology    *TaskTopology
    35  		err         error
    36  	}{
    37  		{
    38  			description: "correct annotation",
    39  			job: &api.JobInfo{
    40  				Name:      "job1",
    41  				Namespace: "default",
    42  				Tasks: map[api.TaskID]*api.TaskInfo{
    43  					"0": {
    44  						Name: "job1-ps-0",
    45  					},
    46  					"1": {
    47  						Name: "job1-ps-1",
    48  					},
    49  					"2": {
    50  						Name: "job1-worker-0",
    51  					},
    52  					"3": {
    53  						Name: "job1-worker-1",
    54  					},
    55  					"4": {
    56  						Name: "job1-chief-0",
    57  					},
    58  					"5": {
    59  						Name: "job1-evaluator-0",
    60  					},
    61  				},
    62  				PodGroup: &api.PodGroup{
    63  					PodGroup: scheduling.PodGroup{
    64  						ObjectMeta: metav1.ObjectMeta{
    65  							Annotations: map[string]string{
    66  								JobAffinityAnnotations:     "ps,worker;ps,chief",
    67  								JobAntiAffinityAnnotations: "ps;worker,chief",
    68  								TaskOrderAnnotations:       "ps,worker,chief,evaluator",
    69  							},
    70  						},
    71  					},
    72  				},
    73  			},
    74  			topology: &TaskTopology{
    75  				Affinity: [][]string{
    76  					{
    77  						"ps",
    78  						"worker",
    79  					},
    80  					{
    81  						"ps",
    82  						"chief",
    83  					},
    84  				},
    85  				AntiAffinity: [][]string{
    86  					{
    87  						"ps",
    88  					},
    89  					{
    90  						"worker",
    91  						"chief",
    92  					},
    93  				},
    94  				TaskOrder: []string{
    95  					"ps",
    96  					"worker",
    97  					"chief",
    98  					"evaluator",
    99  				},
   100  			},
   101  			err: nil,
   102  		},
   103  		{
   104  			description: "correct annotation with tasks whose names contain `-`",
   105  			job: &api.JobInfo{
   106  				Name:      "job1",
   107  				Namespace: "default",
   108  				Tasks: map[api.TaskID]*api.TaskInfo{
   109  					"0": {
   110  						Name: "job1-ps-some-0",
   111  					},
   112  					"1": {
   113  						Name: "job1-ps-some-1",
   114  					},
   115  					"2": {
   116  						Name: "job1-worker-another-some-0",
   117  					},
   118  					"3": {
   119  						Name: "job1-worker-another-some-1",
   120  					},
   121  					"4": {
   122  						Name: "job1-chief-kk-0",
   123  					},
   124  					"5": {
   125  						Name: "job1-evaluator-tt-0",
   126  					},
   127  				},
   128  				PodGroup: &api.PodGroup{
   129  					PodGroup: scheduling.PodGroup{
   130  						ObjectMeta: metav1.ObjectMeta{
   131  							Annotations: map[string]string{
   132  								JobAffinityAnnotations:     "ps-some,worker-another-some;ps-some,chief-kk",
   133  								JobAntiAffinityAnnotations: "ps-some;worker-another-some,chief-kk",
   134  								TaskOrderAnnotations:       "ps-some,worker-another-some,chief-kk,evaluator-tt",
   135  							},
   136  						},
   137  					},
   138  				},
   139  			},
   140  			topology: &TaskTopology{
   141  				Affinity: [][]string{
   142  					{
   143  						"ps-some",
   144  						"worker-another-some",
   145  					},
   146  					{
   147  						"ps-some",
   148  						"chief-kk",
   149  					},
   150  				},
   151  				AntiAffinity: [][]string{
   152  					{
   153  						"ps-some",
   154  					},
   155  					{
   156  						"worker-another-some",
   157  						"chief-kk",
   158  					},
   159  				},
   160  				TaskOrder: []string{
   161  					"ps-some",
   162  					"worker-another-some",
   163  					"chief-kk",
   164  					"evaluator-tt",
   165  				},
   166  			},
   167  			err: nil,
   168  		},
   169  		{
   170  			description: "nil annotation",
   171  			job: &api.JobInfo{
   172  				PodGroup: &api.PodGroup{
   173  					PodGroup: scheduling.PodGroup{
   174  						ObjectMeta: metav1.ObjectMeta{
   175  							Annotations: nil,
   176  						},
   177  					},
   178  				},
   179  			},
   180  			topology: nil,
   181  			err:      nil,
   182  		},
   183  		{
   184  			description: "invalid annotation",
   185  			job: &api.JobInfo{
   186  				Name:      "job1",
   187  				Namespace: "default",
   188  				Tasks: map[api.TaskID]*api.TaskInfo{
   189  					"0": {
   190  						Name: "job1-ps-0",
   191  					},
   192  					"1": {
   193  						Name: "job1-ps-1",
   194  					},
   195  					"2": {
   196  						Name: "job1-worker-0",
   197  					},
   198  					"3": {
   199  						Name: "job1-worker-1",
   200  					},
   201  					"4": {
   202  						Name: "job1-chief-0",
   203  					},
   204  					"5": {
   205  						Name: "job1-evaluator-0",
   206  					},
   207  				},
   208  				PodGroup: &api.PodGroup{
   209  					PodGroup: scheduling.PodGroup{
   210  						ObjectMeta: metav1.ObjectMeta{
   211  							Annotations: map[string]string{
   212  								JobAffinityAnnotations:     "$%GF$^trtqwrg^",
   213  								JobAntiAffinityAnnotations: "ps;worker,chief;#@FGEW^vfa897bgs;;",
   214  								TaskOrderAnnotations:       "ps,worker,chief,evaluator,,,",
   215  							},
   216  						},
   217  					},
   218  				},
   219  			},
   220  			topology: nil,
   221  			err:      fmt.Errorf("task %s do not exist in job <%s/%s>", "$%GF$^trtqwrg^", "default", "job1"),
   222  		},
   223  		{
   224  			description: "invalid task name",
   225  			job: &api.JobInfo{
   226  				Name:      "job1",
   227  				Namespace: "default",
   228  				Tasks: map[api.TaskID]*api.TaskInfo{
   229  					"0": {
   230  						Name: "job1-ps-0",
   231  					},
   232  					"1": {
   233  						Name: "job1-ps-1",
   234  					},
   235  					"2": {
   236  						Name: "job1-worker-0",
   237  					},
   238  					"3": {
   239  						Name: "job1-worker-1",
   240  					},
   241  				},
   242  				PodGroup: &api.PodGroup{
   243  					PodGroup: scheduling.PodGroup{
   244  						ObjectMeta: metav1.ObjectMeta{
   245  							Annotations: map[string]string{
   246  								JobAffinityAnnotations:     "ps,worker",
   247  								JobAntiAffinityAnnotations: "ps",
   248  								TaskOrderAnnotations:       "ps,worker,chief",
   249  							},
   250  						},
   251  					},
   252  				},
   253  			},
   254  			topology: nil,
   255  			err:      fmt.Errorf("task %s do not exist in job <%s/%s>", "chief", "default", "job1"),
   256  		},
   257  		{
   258  			description: "duplicated task name",
   259  			job: &api.JobInfo{
   260  				Name:      "job1",
   261  				Namespace: "default",
   262  				Tasks: map[api.TaskID]*api.TaskInfo{
   263  					"0": {
   264  						Name: "job1-ps-0",
   265  					},
   266  					"1": {
   267  						Name: "job1-ps-1",
   268  					},
   269  					"2": {
   270  						Name: "job1-worker-0",
   271  					},
   272  					"3": {
   273  						Name: "job1-worker-1",
   274  					},
   275  				},
   276  				PodGroup: &api.PodGroup{
   277  					PodGroup: scheduling.PodGroup{
   278  						ObjectMeta: metav1.ObjectMeta{
   279  							Annotations: map[string]string{
   280  								JobAffinityAnnotations:     "ps,worker",
   281  								JobAntiAffinityAnnotations: "ps,ps",
   282  								TaskOrderAnnotations:       "ps,worker",
   283  							},
   284  						},
   285  					},
   286  				},
   287  			},
   288  			topology: nil,
   289  			err:      fmt.Errorf("task %s is duplicated in job <%s/%s>", "ps", "default", "job1"),
   290  		},
   291  		{
   292  			description: "redundant punctuations",
   293  			job: &api.JobInfo{
   294  				Name:      "job1",
   295  				Namespace: "default",
   296  				Tasks: map[api.TaskID]*api.TaskInfo{
   297  					"0": {
   298  						Name: "job1-ps-0",
   299  					},
   300  					"1": {
   301  						Name: "job1-ps-1",
   302  					},
   303  					"2": {
   304  						Name: "job1-worker-0",
   305  					},
   306  					"3": {
   307  						Name: "job1-worker-1",
   308  					},
   309  					"4": {
   310  						Name: "job1-chief-0",
   311  					},
   312  					"5": {
   313  						Name: "job1-evaluator-0",
   314  					},
   315  				},
   316  				PodGroup: &api.PodGroup{
   317  					PodGroup: scheduling.PodGroup{
   318  						ObjectMeta: metav1.ObjectMeta{
   319  							Annotations: map[string]string{
   320  								JobAffinityAnnotations:     "ps,worker;ps,,chief,;",
   321  								JobAntiAffinityAnnotations: "ps;worker,chief;;;",
   322  								TaskOrderAnnotations:       "ps,worker,chief,evaluator,,,",
   323  							},
   324  						},
   325  					},
   326  				},
   327  			},
   328  			topology: &TaskTopology{
   329  				Affinity: [][]string{
   330  					{
   331  						"ps",
   332  						"worker",
   333  					},
   334  					{
   335  						"ps",
   336  						"",
   337  						"chief",
   338  						"",
   339  					},
   340  					{
   341  						"",
   342  					},
   343  				},
   344  				AntiAffinity: [][]string{
   345  					{
   346  						"ps",
   347  					},
   348  					{
   349  						"worker",
   350  						"chief",
   351  					},
   352  					{
   353  						"",
   354  					},
   355  					{
   356  						"",
   357  					},
   358  					{
   359  						"",
   360  					},
   361  				},
   362  				TaskOrder: []string{
   363  					"ps",
   364  					"worker",
   365  					"chief",
   366  					"evaluator",
   367  					"",
   368  					"",
   369  					"",
   370  				},
   371  			},
   372  			err: nil,
   373  		},
   374  	}
   375  
   376  	for i, c := range cases {
   377  		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
   378  			t.Logf("case: %s", c.description)
   379  			topology, err := readTopologyFromPgAnnotations(c.job)
   380  			if !reflect.DeepEqual(err, c.err) {
   381  				t.Errorf("want %v ,got %v", c.err, err)
   382  			}
   383  			if !reflect.DeepEqual(topology, c.topology) {
   384  				t.Errorf("want %v ,got %v", c.topology, topology)
   385  			}
   386  		})
   387  	}
   388  }