volcano.sh/volcano@v1.9.0/test/e2e/schedulingaction/preempt.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 schedulingaction
    18  
    19  import (
    20  	"context"
    21  
    22  	. "github.com/onsi/ginkgo/v2"
    23  	. "github.com/onsi/gomega"
    24  
    25  	corev1 "k8s.io/api/core/v1"
    26  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  
    28  	schedulingv1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    29  
    30  	e2eutil "volcano.sh/volcano/test/e2e/util"
    31  )
    32  
    33  const (
    34  	highPriority        = "high-priority"
    35  	middlePriority      = "middle-priority"
    36  	lowPriority         = "low-priority"
    37  	highPriorityValue   = 100
    38  	middlePriorityValue = 50
    39  	lowPriorityValue    = 10
    40  )
    41  
    42  var _ = Describe("Job E2E Test", func() {
    43  	It("schedule high priority job without preemption when resource is enough", func() {
    44  		ctx := e2eutil.InitTestContext(e2eutil.Options{
    45  			PriorityClasses: map[string]int32{
    46  				highPriority: highPriorityValue,
    47  				lowPriority:  lowPriorityValue,
    48  			},
    49  		})
    50  		defer e2eutil.CleanupTestContext(ctx)
    51  
    52  		slot := e2eutil.OneCPU
    53  
    54  		job := &e2eutil.JobSpec{
    55  			Tasks: []e2eutil.TaskSpec{
    56  				{
    57  					Img:    e2eutil.DefaultNginxImage,
    58  					Req:    slot,
    59  					Min:    1,
    60  					Rep:    1,
    61  					Labels: map[string]string{schedulingv1beta1.PodPreemptable: "true"},
    62  				},
    63  			},
    64  		}
    65  
    66  		job.Name = "preemptee"
    67  		job.Pri = lowPriority
    68  		preempteeJob := e2eutil.CreateJob(ctx, job)
    69  		err := e2eutil.WaitTasksReady(ctx, preempteeJob, 1)
    70  		Expect(err).NotTo(HaveOccurred())
    71  
    72  		job.Name = "preemptor"
    73  		job.Pri = highPriority
    74  		preemptorJob := e2eutil.CreateJob(ctx, job)
    75  		err = e2eutil.WaitTasksReady(ctx, preempteeJob, 1)
    76  		Expect(err).NotTo(HaveOccurred())
    77  
    78  		err = e2eutil.WaitTasksReady(ctx, preemptorJob, 1)
    79  		Expect(err).NotTo(HaveOccurred())
    80  	})
    81  
    82  	It("schedule high priority job with preemption when idle resource is NOT enough but preemptee resource is enough", func() {
    83  		ctx := e2eutil.InitTestContext(e2eutil.Options{
    84  			PriorityClasses: map[string]int32{
    85  				highPriority: highPriorityValue,
    86  				lowPriority:  lowPriorityValue,
    87  			},
    88  		})
    89  		defer e2eutil.CleanupTestContext(ctx)
    90  
    91  		slot := e2eutil.OneCPU
    92  		rep := e2eutil.ClusterSize(ctx, slot)
    93  
    94  		job := &e2eutil.JobSpec{
    95  			Tasks: []e2eutil.TaskSpec{
    96  				{
    97  					Img:    e2eutil.DefaultNginxImage,
    98  					Req:    slot,
    99  					Min:    1,
   100  					Rep:    rep,
   101  					Labels: map[string]string{schedulingv1beta1.PodPreemptable: "true"},
   102  				},
   103  			},
   104  		}
   105  
   106  		job.Name = "preemptee"
   107  		job.Pri = lowPriority
   108  		preempteeJob := e2eutil.CreateJob(ctx, job)
   109  		err := e2eutil.WaitTasksReady(ctx, preempteeJob, int(rep))
   110  		Expect(err).NotTo(HaveOccurred())
   111  
   112  		job.Name = "preemptor"
   113  		job.Pri = highPriority
   114  		job.Min = rep / 2
   115  		preemptorJob := e2eutil.CreateJob(ctx, job)
   116  		err = e2eutil.WaitTasksReady(ctx, preempteeJob, int(rep)/2)
   117  		Expect(err).NotTo(HaveOccurred())
   118  
   119  		err = e2eutil.WaitTasksReady(ctx, preemptorJob, int(rep)/2)
   120  		Expect(err).NotTo(HaveOccurred())
   121  	})
   122  
   123  	It("preemption doesn't work when podgroup is pending due to insufficient resource", func() {
   124  		ctx := e2eutil.InitTestContext(e2eutil.Options{
   125  			PriorityClasses: map[string]int32{
   126  				highPriority: highPriorityValue,
   127  				lowPriority:  lowPriorityValue,
   128  			},
   129  		})
   130  		defer e2eutil.CleanupTestContext(ctx)
   131  
   132  		pgName := "pending-pg"
   133  		pg := &schedulingv1beta1.PodGroup{
   134  			ObjectMeta: v1.ObjectMeta{
   135  				Namespace: ctx.Namespace,
   136  				Name:      pgName,
   137  			},
   138  			Spec: schedulingv1beta1.PodGroupSpec{
   139  				MinMember:    1,
   140  				MinResources: &e2eutil.ThirtyCPU,
   141  			},
   142  		}
   143  		_, err := ctx.Vcclient.SchedulingV1beta1().PodGroups(ctx.Namespace).Create(context.TODO(), pg, v1.CreateOptions{})
   144  		Expect(err).NotTo(HaveOccurred())
   145  
   146  		slot := e2eutil.OneCPU
   147  		rep := e2eutil.ClusterSize(ctx, slot)
   148  		job := &e2eutil.JobSpec{
   149  			Tasks: []e2eutil.TaskSpec{
   150  				{
   151  					Img: e2eutil.DefaultNginxImage,
   152  					Req: slot,
   153  					Min: 1,
   154  					Rep: rep,
   155  				},
   156  			},
   157  		}
   158  		job.Name = "preemptee"
   159  		job.Pri = lowPriority
   160  		preempteeJob := e2eutil.CreateJob(ctx, job)
   161  		err = e2eutil.WaitTasksReady(ctx, preempteeJob, int(rep))
   162  		Expect(err).NotTo(HaveOccurred())
   163  
   164  		pod := &corev1.Pod{
   165  			TypeMeta: v1.TypeMeta{
   166  				APIVersion: "v1",
   167  				Kind:       "Pod",
   168  			},
   169  			ObjectMeta: v1.ObjectMeta{
   170  				Namespace:   ctx.Namespace,
   171  				Name:        "preemptor-pod",
   172  				Annotations: map[string]string{schedulingv1beta1.KubeGroupNameAnnotationKey: pgName},
   173  			},
   174  			Spec: corev1.PodSpec{
   175  				SchedulerName:     "volcano",
   176  				Containers:        e2eutil.CreateContainers(e2eutil.DefaultNginxImage, "", "", e2eutil.OneCPU, e2eutil.OneCPU, 0),
   177  				PriorityClassName: highPriority,
   178  			},
   179  		}
   180  		// Pod is allowed to be created, preemption does not happen due to PodGroup is in pending state
   181  		_, err = ctx.Kubeclient.CoreV1().Pods(ctx.Namespace).Create(context.TODO(), pod, v1.CreateOptions{})
   182  		Expect(err).NotTo(HaveOccurred())
   183  		// Make sure preempteeJob is not preempted as expected
   184  		err = e2eutil.WaitTasksReady(ctx, preempteeJob, int(rep))
   185  		Expect(err).NotTo(HaveOccurred())
   186  	})
   187  
   188  	It("preemption only works in the same queue", func() {
   189  		ctx := e2eutil.InitTestContext(e2eutil.Options{
   190  			Queues: []string{"q1-preemption", "q2-reference"},
   191  			PriorityClasses: map[string]int32{
   192  				highPriority: highPriorityValue,
   193  				lowPriority:  lowPriorityValue,
   194  			},
   195  		})
   196  		defer e2eutil.CleanupTestContext(ctx)
   197  
   198  		slot := e2eutil.OneCPU
   199  		rep := e2eutil.ClusterSize(ctx, slot)
   200  		job := &e2eutil.JobSpec{
   201  			Tasks: []e2eutil.TaskSpec{
   202  				{
   203  					Img:    e2eutil.DefaultNginxImage,
   204  					Req:    slot,
   205  					Min:    1,
   206  					Rep:    rep / 2,
   207  					Labels: map[string]string{schedulingv1beta1.PodPreemptable: "true"},
   208  				},
   209  			},
   210  		}
   211  
   212  		job.Name = "j1-q1"
   213  		job.Pri = lowPriority
   214  		job.Queue = "q1-preemption"
   215  		queue1Job := e2eutil.CreateJob(ctx, job)
   216  		err := e2eutil.WaitTasksReady(ctx, queue1Job, int(rep)/2)
   217  		Expect(err).NotTo(HaveOccurred())
   218  
   219  		job.Name = "j2-q2"
   220  		job.Pri = lowPriority
   221  		job.Queue = "q2-reference"
   222  		queue2Job := e2eutil.CreateJob(ctx, job)
   223  		err = e2eutil.WaitTasksReady(ctx, queue2Job, int(rep)/2)
   224  		Expect(err).NotTo(HaveOccurred())
   225  
   226  		job.Name = "j3-q1"
   227  		job.Pri = highPriority
   228  		job.Queue = "q1-preemption"
   229  		job.Tasks[0].Rep = rep
   230  		queue1Job3 := e2eutil.CreateJob(ctx, job)
   231  		err = e2eutil.WaitTasksReady(ctx, queue1Job3, int(rep)/2)
   232  		Expect(err).NotTo(HaveOccurred())
   233  		err = e2eutil.WaitTasksReady(ctx, queue1Job, 0)
   234  		Expect(err).NotTo(HaveOccurred())
   235  	})
   236  
   237  	It("preemption doesn't work when total resource of idle resource and preemptee is NOT enough", func() {
   238  		ctx := e2eutil.InitTestContext(e2eutil.Options{
   239  			Queues: []string{"q1-preemption", "q2-reference"},
   240  			PriorityClasses: map[string]int32{
   241  				highPriority: highPriorityValue,
   242  				lowPriority:  lowPriorityValue,
   243  			},
   244  		})
   245  		defer e2eutil.CleanupTestContext(ctx)
   246  
   247  		slot := e2eutil.OneCPU
   248  		rep := e2eutil.ClusterSize(ctx, slot)
   249  		job := &e2eutil.JobSpec{
   250  			Tasks: []e2eutil.TaskSpec{
   251  				{
   252  					Img:    e2eutil.DefaultNginxImage,
   253  					Req:    slot,
   254  					Min:    1,
   255  					Rep:    1,
   256  					Labels: map[string]string{schedulingv1beta1.PodPreemptable: "true"},
   257  				},
   258  			},
   259  		}
   260  
   261  		job.Name = "j1-q1"
   262  		job.Pri = lowPriority
   263  		job.Queue = "q1-preemption"
   264  		queue1Job := e2eutil.CreateJob(ctx, job)
   265  		err := e2eutil.WaitTasksReady(ctx, queue1Job, 1)
   266  		Expect(err).NotTo(HaveOccurred())
   267  
   268  		job.Name = "j2-q2"
   269  		job.Pri = lowPriority
   270  		job.Queue = "q2-reference"
   271  		job.Tasks[0].Min = rep / 2
   272  		job.Tasks[0].Rep = rep / 2
   273  		queue2Job := e2eutil.CreateJob(ctx, job)
   274  		err = e2eutil.WaitTasksReady(ctx, queue2Job, int(rep)/2)
   275  		Expect(err).NotTo(HaveOccurred())
   276  
   277  		job.Name = "j3-q1"
   278  		job.Pri = highPriority
   279  		job.Queue = "q1-preemption"
   280  		job.Tasks[0].Min = rep
   281  		job.Tasks[0].Rep = rep
   282  		queue1Job3 := e2eutil.CreateJob(ctx, job)
   283  		err = e2eutil.WaitTasksReady(ctx, queue1Job3, int(rep))
   284  		Expect(err).To(HaveOccurred())
   285  		err = e2eutil.WaitTasksReady(ctx, queue1Job, 1)
   286  		Expect(err).NotTo(HaveOccurred())
   287  		err = e2eutil.WaitTasksReady(ctx, queue2Job, int(rep)/2)
   288  		Expect(err).NotTo(HaveOccurred())
   289  	})
   290  
   291  	It("multi-preemptor-jobs who are in different priority", func() {
   292  		Skip("https://github.com/volcano-sh/volcano/issues/911")
   293  		ctx := e2eutil.InitTestContext(e2eutil.Options{
   294  			Queues: []string{"q1-preemption"},
   295  			PriorityClasses: map[string]int32{
   296  				highPriority:   highPriorityValue,
   297  				middlePriority: middlePriorityValue,
   298  				lowPriority:    lowPriorityValue,
   299  			},
   300  		})
   301  		defer e2eutil.CleanupTestContext(ctx)
   302  
   303  		slot := e2eutil.OneCPU
   304  		rep := e2eutil.ClusterSize(ctx, slot)
   305  		job := &e2eutil.JobSpec{
   306  			Tasks: []e2eutil.TaskSpec{
   307  				{
   308  					Img:    e2eutil.DefaultNginxImage,
   309  					Req:    slot,
   310  					Min:    1,
   311  					Rep:    rep,
   312  					Labels: map[string]string{schedulingv1beta1.PodPreemptable: "true"},
   313  				},
   314  			},
   315  		}
   316  
   317  		job.Name = "low-priority-job"
   318  		job.Pri = lowPriority
   319  		job.Queue = "q1-preemption"
   320  		lowPriorityJob := e2eutil.CreateJob(ctx, job)
   321  		err := e2eutil.WaitTasksReady(ctx, lowPriorityJob, int(rep))
   322  		Expect(err).NotTo(HaveOccurred())
   323  
   324  		job.Name = "middle-prority-job"
   325  		job.Pri = middlePriority
   326  		job.Queue = "q1-preemption"
   327  		job.Tasks[0].Rep = rep / 2
   328  		job.Tasks[0].Min = rep / 2
   329  		middlePriorityJob := e2eutil.CreateJob(ctx, job)
   330  		err = e2eutil.WaitTasksReady(ctx, middlePriorityJob, int(rep)/2)
   331  		Expect(err).NotTo(HaveOccurred())
   332  		err = e2eutil.WaitTasksReady(ctx, lowPriorityJob, int(rep)/2)
   333  		Expect(err).NotTo(HaveOccurred())
   334  
   335  		job.Name = "high-priority-job"
   336  		job.Pri = highPriority
   337  		job.Queue = "q1-preemption"
   338  		job.Tasks[0].Rep = rep
   339  		job.Tasks[0].Min = rep
   340  		highPriorityJob := e2eutil.CreateJob(ctx, job)
   341  		err = e2eutil.WaitTasksReady(ctx, highPriorityJob, int(rep))
   342  		Expect(err).NotTo(HaveOccurred())
   343  		err = e2eutil.WaitTasksReady(ctx, lowPriorityJob, 0)
   344  		Expect(err).NotTo(HaveOccurred())
   345  		err = e2eutil.WaitTasksReady(ctx, middlePriorityJob, 0)
   346  		Expect(err).NotTo(HaveOccurred())
   347  	})
   348  })