volcano.sh/volcano@v1.9.0/pkg/webhooks/admission/pods/validate/admit_pod_test.go (about)

     1  /*
     2  Copyright 2019 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 validate
    18  
    19  import (
    20  	"context"
    21  	"strings"
    22  	"testing"
    23  
    24  	admissionv1 "k8s.io/api/admission/v1"
    25  	v1 "k8s.io/api/core/v1"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  
    28  	vcschedulingv1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    29  	vcclient "volcano.sh/apis/pkg/client/clientset/versioned/fake"
    30  )
    31  
    32  func TestValidatePod(t *testing.T) {
    33  
    34  	namespace := "test"
    35  	pgName := "podgroup-p1"
    36  
    37  	testCases := []struct {
    38  		Name           string
    39  		Pod            v1.Pod
    40  		ExpectErr      bool
    41  		reviewResponse admissionv1.AdmissionResponse
    42  		ret            string
    43  		disabledPG     bool
    44  		queueName      string
    45  		queueState     vcschedulingv1.QueueState
    46  	}{
    47  		// validate normal pod with default-scheduler
    48  		{
    49  			Name: "validate default normal pod",
    50  			Pod: v1.Pod{
    51  				TypeMeta: metav1.TypeMeta{
    52  					APIVersion: "v1",
    53  					Kind:       "Pod",
    54  				},
    55  				ObjectMeta: metav1.ObjectMeta{
    56  					Namespace: namespace,
    57  					Name:      "normal-pod-1",
    58  				},
    59  				Spec: v1.PodSpec{
    60  					SchedulerName: "default-scheduler",
    61  				},
    62  			},
    63  
    64  			reviewResponse: admissionv1.AdmissionResponse{Allowed: true},
    65  			ret:            "",
    66  			ExpectErr:      false,
    67  		},
    68  		// validate volcano pod with volcano scheduler when get pg failed
    69  		{
    70  			Name: "validate volcano volcano pod when get pg failed",
    71  			Pod: v1.Pod{
    72  				TypeMeta: metav1.TypeMeta{
    73  					APIVersion: "v1",
    74  					Kind:       "Pod",
    75  				},
    76  				ObjectMeta: metav1.ObjectMeta{
    77  					Namespace:   namespace,
    78  					Name:        "volcano-pod-2",
    79  					Annotations: map[string]string{vcschedulingv1.KubeGroupNameAnnotationKey: pgName},
    80  				},
    81  				Spec: v1.PodSpec{
    82  					SchedulerName: "volcano",
    83  				},
    84  			},
    85  
    86  			reviewResponse: admissionv1.AdmissionResponse{Allowed: false},
    87  			ret:            `failed to get PodGroup for pod <test/volcano-pod-2>: podgroups.scheduling.volcano.sh "podgroup-p1" not found`,
    88  			ExpectErr:      true,
    89  			disabledPG:     true,
    90  		},
    91  		// validate volcano pod with volcano scheduler when queue is closed when no pg
    92  		{
    93  			Name: "validate pod when volcano queue is closed when no pg",
    94  			Pod: v1.Pod{
    95  				TypeMeta: metav1.TypeMeta{
    96  					APIVersion: "v1",
    97  					Kind:       "Pod",
    98  				},
    99  				ObjectMeta: metav1.ObjectMeta{
   100  					Namespace:   namespace,
   101  					Name:        "volcano-pod-3",
   102  					Annotations: map[string]string{vcschedulingv1.QueueNameAnnotationKey: "queue-closed"},
   103  				},
   104  				Spec: v1.PodSpec{
   105  					SchedulerName: "volcano",
   106  				},
   107  			},
   108  
   109  			reviewResponse: admissionv1.AdmissionResponse{Allowed: false},
   110  			ret:            "can only submit job to queue with state `Open`, queue `queue-closed` status is `Closed`",
   111  			ExpectErr:      true,
   112  			queueState:     vcschedulingv1.QueueStateClosed,
   113  			queueName:      "queue-closed",
   114  		},
   115  		// validate volcano pod with volcano scheduler when queue is Open when no pg
   116  		{
   117  			Name: "validate pod when volcano queue is open when no pg",
   118  			Pod: v1.Pod{
   119  				TypeMeta: metav1.TypeMeta{
   120  					APIVersion: "v1",
   121  					Kind:       "Pod",
   122  				},
   123  				ObjectMeta: metav1.ObjectMeta{
   124  					Namespace:   namespace,
   125  					Name:        "volcano-pod-4",
   126  					Annotations: map[string]string{vcschedulingv1.QueueNameAnnotationKey: "queue-open"},
   127  				},
   128  				Spec: v1.PodSpec{
   129  					SchedulerName: "volcano",
   130  				},
   131  			},
   132  
   133  			reviewResponse: admissionv1.AdmissionResponse{Allowed: true},
   134  			ret:            "",
   135  			ExpectErr:      false,
   136  			queueState:     vcschedulingv1.QueueStateOpen,
   137  			queueName:      "queue-open",
   138  		},
   139  		// validate volcano pod with volcano scheduler when queue is Open when pg
   140  		{
   141  			Name: "validate pod when volcano queue is open when pg",
   142  			Pod: v1.Pod{
   143  				TypeMeta: metav1.TypeMeta{
   144  					APIVersion: "v1",
   145  					Kind:       "Pod",
   146  				},
   147  				ObjectMeta: metav1.ObjectMeta{
   148  					Namespace:   namespace,
   149  					Name:        "volcano-pod-5",
   150  					Annotations: map[string]string{vcschedulingv1.KubeGroupNameAnnotationKey: pgName},
   151  				},
   152  				Spec: v1.PodSpec{
   153  					SchedulerName: "volcano",
   154  				},
   155  			},
   156  
   157  			reviewResponse: admissionv1.AdmissionResponse{Allowed: true},
   158  			ret:            "",
   159  			ExpectErr:      false,
   160  			queueName:      "queue-open",
   161  			queueState:     vcschedulingv1.QueueStateOpen,
   162  		},
   163  		// validate volcano pod with volcano scheduler when queue is Closed when pg
   164  		{
   165  			Name: "validate pod when volcano queue is closed when pg",
   166  			Pod: v1.Pod{
   167  				TypeMeta: metav1.TypeMeta{
   168  					APIVersion: "v1",
   169  					Kind:       "Pod",
   170  				},
   171  				ObjectMeta: metav1.ObjectMeta{
   172  					Namespace:   namespace,
   173  					Name:        "volcano-pod-6",
   174  					Annotations: map[string]string{vcschedulingv1.KubeGroupNameAnnotationKey: pgName},
   175  				},
   176  				Spec: v1.PodSpec{
   177  					SchedulerName: "volcano",
   178  				},
   179  			},
   180  
   181  			reviewResponse: admissionv1.AdmissionResponse{Allowed: true},
   182  			ret:            "can only submit job to queue with state `Open`, queue `queue-closed` status is `Closed`",
   183  			ExpectErr:      true,
   184  			queueName:      "queue-closed",
   185  			queueState:     vcschedulingv1.QueueStateClosed,
   186  		},
   187  		// validate volcano pod with volcano scheduler when no queue and no pg
   188  		{
   189  			Name: "validate volcano pod with volcano scheduler when no queue and no pg",
   190  			Pod: v1.Pod{
   191  				TypeMeta: metav1.TypeMeta{
   192  					APIVersion: "v1",
   193  					Kind:       "Pod",
   194  				},
   195  				ObjectMeta: metav1.ObjectMeta{
   196  					Namespace: namespace,
   197  					Name:      "volcano-pod-7",
   198  				},
   199  				Spec: v1.PodSpec{
   200  					SchedulerName: "volcano",
   201  				},
   202  			},
   203  
   204  			reviewResponse: admissionv1.AdmissionResponse{Allowed: true},
   205  			ret:            "",
   206  			ExpectErr:      false,
   207  			disabledPG:     true,
   208  		},
   209  		// validate volcano pod with volcano scheduler when queue name is empty and when pg
   210  		{
   211  			Name: "validate volcano pod with volcano scheduler when no queue and no pg",
   212  			Pod: v1.Pod{
   213  				TypeMeta: metav1.TypeMeta{
   214  					APIVersion: "v1",
   215  					Kind:       "Pod",
   216  				},
   217  				ObjectMeta: metav1.ObjectMeta{
   218  					Namespace:   namespace,
   219  					Name:        "volcano-pod-8",
   220  					Annotations: map[string]string{vcschedulingv1.KubeGroupNameAnnotationKey: pgName},
   221  				},
   222  				Spec: v1.PodSpec{
   223  					SchedulerName: "volcano",
   224  				},
   225  			},
   226  
   227  			reviewResponse: admissionv1.AdmissionResponse{Allowed: true},
   228  			ret:            "",
   229  			ExpectErr:      false,
   230  			queueName:      "",
   231  		},
   232  	}
   233  
   234  	for _, testCase := range testCases {
   235  
   236  		pg := &vcschedulingv1.PodGroup{
   237  			ObjectMeta: metav1.ObjectMeta{
   238  				Namespace: namespace,
   239  				Name:      "podgroup-p1",
   240  			},
   241  			Spec: vcschedulingv1.PodGroupSpec{
   242  				MinMember: 1,
   243  				Queue:     testCase.queueName,
   244  			},
   245  			Status: vcschedulingv1.PodGroupStatus{
   246  				Phase: vcschedulingv1.PodGroupPending,
   247  			},
   248  		}
   249  		queue := vcschedulingv1.Queue{
   250  			ObjectMeta: metav1.ObjectMeta{
   251  				Name: testCase.queueName,
   252  			},
   253  			Spec: vcschedulingv1.QueueSpec{
   254  				Weight: 1,
   255  			},
   256  			Status: vcschedulingv1.QueueStatus{
   257  				State: testCase.queueState,
   258  			},
   259  		}
   260  
   261  		// create fake volcano clientset
   262  		config.VolcanoClient = vcclient.NewSimpleClientset()
   263  		config.SchedulerNames = []string{"volcano"}
   264  
   265  		if !testCase.disabledPG {
   266  			_, err := config.VolcanoClient.SchedulingV1beta1().PodGroups(namespace).Create(context.TODO(), pg, metav1.CreateOptions{})
   267  			if err != nil {
   268  				t.Error("PG Creation Failed")
   269  			}
   270  		}
   271  
   272  		if testCase.queueName != "" && testCase.queueState != "" {
   273  			//create default queue
   274  			_, err := config.VolcanoClient.SchedulingV1beta1().Queues().Create(context.TODO(), &queue, metav1.CreateOptions{})
   275  			if err != nil {
   276  				t.Error("Queue Creation Failed")
   277  			}
   278  		}
   279  
   280  		ret := validatePod(&testCase.Pod, &testCase.reviewResponse)
   281  
   282  		if testCase.ExpectErr == true && ret == "" {
   283  			t.Errorf("%s: test case Expect error msg :%s, but got nil.", testCase.Name, testCase.ret)
   284  		}
   285  		if testCase.ExpectErr == true && testCase.reviewResponse.Allowed != false {
   286  			t.Errorf("%s: test case Expect Allowed as false but got true.", testCase.Name)
   287  		}
   288  		if testCase.ExpectErr == true && !strings.Contains(ret, testCase.ret) {
   289  			t.Errorf("%s: test case Expect error msg :%s, but got diff error %v", testCase.Name, testCase.ret, ret)
   290  		}
   291  
   292  		if testCase.ExpectErr == false && ret != "" {
   293  			t.Errorf("%s: test case Expect no error, but got error %v", testCase.Name, ret)
   294  		}
   295  		if testCase.ExpectErr == false && testCase.reviewResponse.Allowed != true {
   296  			t.Errorf("%s: test case Expect Allowed as true but got false. %v", testCase.Name, testCase.reviewResponse)
   297  		}
   298  	}
   299  }