volcano.sh/volcano@v1.9.0/pkg/webhooks/admission/queues/validate/validate_queue_test.go (about)

     1  /*
     2  Copyright 2018 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  	"encoding/json"
    22  	"fmt"
    23  	"reflect"
    24  	"testing"
    25  
    26  	admissionv1 "k8s.io/api/admission/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/util/validation/field"
    30  
    31  	schedulingv1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1"
    32  	fakeclient "volcano.sh/apis/pkg/client/clientset/versioned/fake"
    33  	"volcano.sh/volcano/pkg/webhooks/util"
    34  )
    35  
    36  func TestAdmitQueues(t *testing.T) {
    37  
    38  	stateNotSet := schedulingv1beta1.Queue{
    39  		ObjectMeta: metav1.ObjectMeta{
    40  			Name: "normal-case-not-set",
    41  		},
    42  		Spec: schedulingv1beta1.QueueSpec{
    43  			Weight: 1,
    44  		},
    45  	}
    46  
    47  	stateNotSetJSON, err := json.Marshal(stateNotSet)
    48  	if err != nil {
    49  		t.Errorf("Marshal queue without state set failed for %v.", err)
    50  	}
    51  
    52  	openState := schedulingv1beta1.Queue{
    53  		ObjectMeta: metav1.ObjectMeta{
    54  			Name: "normal-case-set-open",
    55  		},
    56  		Spec: schedulingv1beta1.QueueSpec{
    57  			Weight: 1,
    58  		},
    59  		Status: schedulingv1beta1.QueueStatus{
    60  			State: schedulingv1beta1.QueueStateOpen,
    61  		},
    62  	}
    63  
    64  	openStateJSON, err := json.Marshal(openState)
    65  	if err != nil {
    66  		t.Errorf("Marshal queue with open state failed for %v.", err)
    67  	}
    68  
    69  	closedState := schedulingv1beta1.Queue{
    70  		ObjectMeta: metav1.ObjectMeta{
    71  			Name: "normal-case-set-closed",
    72  		},
    73  		Spec: schedulingv1beta1.QueueSpec{
    74  			Weight: 1,
    75  		},
    76  		Status: schedulingv1beta1.QueueStatus{
    77  			State: schedulingv1beta1.QueueStateClosed,
    78  		},
    79  	}
    80  
    81  	closedStateJSON, err := json.Marshal(closedState)
    82  	if err != nil {
    83  		t.Errorf("Marshal queue with closed state failed for %v.", err)
    84  	}
    85  
    86  	wrongState := schedulingv1beta1.Queue{
    87  		ObjectMeta: metav1.ObjectMeta{
    88  			Name: "abnormal-case",
    89  		},
    90  		Spec: schedulingv1beta1.QueueSpec{
    91  			Weight: 1,
    92  		},
    93  		Status: schedulingv1beta1.QueueStatus{
    94  			State: "wrong",
    95  		},
    96  	}
    97  
    98  	wrongStateJSON, err := json.Marshal(wrongState)
    99  	if err != nil {
   100  		t.Errorf("Marshal queue with wrong state failed for %v.", err)
   101  	}
   102  
   103  	openStateForDelete := schedulingv1beta1.Queue{
   104  		ObjectMeta: metav1.ObjectMeta{
   105  			Name: "open-state-for-delete",
   106  		},
   107  		Spec: schedulingv1beta1.QueueSpec{
   108  			Weight: 1,
   109  		},
   110  		Status: schedulingv1beta1.QueueStatus{
   111  			State: schedulingv1beta1.QueueStateOpen,
   112  		},
   113  	}
   114  
   115  	openStateForDeleteJSON, err := json.Marshal(openStateForDelete)
   116  	if err != nil {
   117  		t.Errorf("Marshal queue for delete with open state failed for %v.", err)
   118  	}
   119  
   120  	closedStateForDelete := schedulingv1beta1.Queue{
   121  		ObjectMeta: metav1.ObjectMeta{
   122  			Name: "closed-state-for-delete",
   123  		},
   124  		Spec: schedulingv1beta1.QueueSpec{
   125  			Weight: 1,
   126  		},
   127  		Status: schedulingv1beta1.QueueStatus{
   128  			State: schedulingv1beta1.QueueStateClosed,
   129  		},
   130  	}
   131  
   132  	closedStateForDeleteJSON, err := json.Marshal(closedStateForDelete)
   133  	if err != nil {
   134  		t.Errorf("Marshal queue for delete with closed state failed for %v.", err)
   135  	}
   136  
   137  	weightNotSet := schedulingv1beta1.Queue{
   138  		ObjectMeta: metav1.ObjectMeta{
   139  			Name: "weight-not-set",
   140  		},
   141  		Spec: schedulingv1beta1.QueueSpec{},
   142  	}
   143  
   144  	weightNotSetJSON, err := json.Marshal(weightNotSet)
   145  	if err != nil {
   146  		t.Errorf("Marshal queue with no weight failed for %v.", err)
   147  	}
   148  
   149  	negativeWeight := schedulingv1beta1.Queue{
   150  		ObjectMeta: metav1.ObjectMeta{
   151  			Name: "negative-weight",
   152  		},
   153  		Spec: schedulingv1beta1.QueueSpec{
   154  			Weight: -1,
   155  		},
   156  	}
   157  
   158  	negativeWeightJSON, err := json.Marshal(negativeWeight)
   159  	if err != nil {
   160  		t.Errorf("Marshal queue with negative weight failed for %v.", err)
   161  	}
   162  
   163  	positiveWeightForUpdate := schedulingv1beta1.Queue{
   164  		ObjectMeta: metav1.ObjectMeta{
   165  			Name: "positive-weight-for-update",
   166  		},
   167  		Spec: schedulingv1beta1.QueueSpec{
   168  			Weight: 1,
   169  		},
   170  	}
   171  	positiveWeightForUpdateJSON, err := json.Marshal(positiveWeightForUpdate)
   172  	if err != nil {
   173  		t.Errorf("Marshal queue with positive weight failed for %v.", err)
   174  	}
   175  
   176  	negativeWeightForUpdate := schedulingv1beta1.Queue{
   177  		ObjectMeta: metav1.ObjectMeta{
   178  			Name: "positive-weight-for-update",
   179  		},
   180  		Spec: schedulingv1beta1.QueueSpec{
   181  			Weight: -1,
   182  		},
   183  	}
   184  
   185  	negativeWeightForUpdateJSON, err := json.Marshal(negativeWeightForUpdate)
   186  	if err != nil {
   187  		t.Errorf("Marshal queue with negative weight failed for %v.", err)
   188  
   189  	}
   190  
   191  	hierarchyWeightsDontMatch := schedulingv1beta1.Queue{
   192  		ObjectMeta: metav1.ObjectMeta{
   193  			Name: "hierarchy-weights-dont-match",
   194  			Annotations: map[string]string{
   195  				schedulingv1beta1.KubeHierarchyAnnotationKey:       "root/a/b",
   196  				schedulingv1beta1.KubeHierarchyWeightAnnotationKey: "1/2/3/4",
   197  			},
   198  		},
   199  		Spec: schedulingv1beta1.QueueSpec{
   200  			Weight: 1,
   201  		},
   202  	}
   203  
   204  	hierarchyWeightsDontMatchJSON, err := json.Marshal(hierarchyWeightsDontMatch)
   205  	if err != nil {
   206  		t.Errorf("Marshal hierarchyWeightsDontMatch failed for %v.", err)
   207  	}
   208  
   209  	hierarchyWeightsNegative := schedulingv1beta1.Queue{
   210  		ObjectMeta: metav1.ObjectMeta{
   211  			Name: "hierarchy-weights-dont-match",
   212  			Annotations: map[string]string{
   213  				schedulingv1beta1.KubeHierarchyAnnotationKey:       "root/a/b",
   214  				schedulingv1beta1.KubeHierarchyWeightAnnotationKey: "1/-1/3",
   215  			},
   216  		},
   217  		Spec: schedulingv1beta1.QueueSpec{
   218  			Weight: 1,
   219  		},
   220  	}
   221  	hierarchyWeightsNegativeJSON, err := json.Marshal(hierarchyWeightsNegative)
   222  	if err != nil {
   223  		t.Errorf("Marshal weightsFormatNegative failed for %v.", err)
   224  	}
   225  
   226  	weightsFormatIllegal := schedulingv1beta1.Queue{
   227  		ObjectMeta: metav1.ObjectMeta{
   228  			Name: "hierarchy-weights-dont-match",
   229  			Annotations: map[string]string{
   230  				schedulingv1beta1.KubeHierarchyAnnotationKey:       "root/a/b",
   231  				schedulingv1beta1.KubeHierarchyWeightAnnotationKey: "1/a/3",
   232  			},
   233  		},
   234  		Spec: schedulingv1beta1.QueueSpec{
   235  			Weight: 1,
   236  		},
   237  	}
   238  
   239  	weightsFormatIllegalJSON, err := json.Marshal(weightsFormatIllegal)
   240  	if err != nil {
   241  		t.Errorf("Marshal weightsFormatIllegal failed for %v.", err)
   242  	}
   243  
   244  	ordinaryHierchicalQueue := schedulingv1beta1.Queue{
   245  		ObjectMeta: metav1.ObjectMeta{
   246  			Name: "ordinary-hierarchical-queue",
   247  			Annotations: map[string]string{
   248  				schedulingv1beta1.KubeHierarchyAnnotationKey:       "root/node1/node2",
   249  				schedulingv1beta1.KubeHierarchyWeightAnnotationKey: "1/2/3",
   250  			},
   251  		},
   252  		Spec: schedulingv1beta1.QueueSpec{
   253  			Weight: 1,
   254  		},
   255  	}
   256  
   257  	hierarchicalQueueInSubPathOfAnotherQueue := schedulingv1beta1.Queue{
   258  		ObjectMeta: metav1.ObjectMeta{
   259  			Name: "hierarchical-queue-in-sub-path-of-another-queue",
   260  			Annotations: map[string]string{
   261  				schedulingv1beta1.KubeHierarchyAnnotationKey:       "root/node1",
   262  				schedulingv1beta1.KubeHierarchyWeightAnnotationKey: "1/4",
   263  			},
   264  		},
   265  		Spec: schedulingv1beta1.QueueSpec{
   266  			Weight: 1,
   267  		},
   268  	}
   269  	hierarchicalQueueInSubPathOfAnotherQueueJSON, err := json.Marshal(hierarchicalQueueInSubPathOfAnotherQueue)
   270  	if err != nil {
   271  		t.Errorf("Marshal hierarchicalQueueInSubPathOfAnotherQueue failed for %v.", err)
   272  	}
   273  
   274  	config.VolcanoClient = fakeclient.NewSimpleClientset()
   275  	_, err = config.VolcanoClient.SchedulingV1beta1().Queues().Create(context.TODO(), &openStateForDelete, metav1.CreateOptions{})
   276  	if err != nil {
   277  		t.Errorf("Create queue with open state failed for %v.", err)
   278  	}
   279  
   280  	_, err = config.VolcanoClient.SchedulingV1beta1().Queues().Create(context.TODO(), &closedStateForDelete, metav1.CreateOptions{})
   281  	if err != nil {
   282  		t.Errorf("Create queue with closed state failed for %v.", err)
   283  	}
   284  
   285  	_, err = config.VolcanoClient.SchedulingV1beta1().Queues().Create(context.TODO(), &ordinaryHierchicalQueue, metav1.CreateOptions{})
   286  	if err != nil {
   287  		t.Errorf("Create hierarchical queue failed for %v.", err)
   288  	}
   289  	_, err = config.VolcanoClient.SchedulingV1beta1().Queues().Create(context.TODO(), &positiveWeightForUpdate, metav1.CreateOptions{})
   290  	if err != nil {
   291  		t.Errorf("Crate queue with positive weight failed for %v.", err)
   292  	}
   293  
   294  	defer func() {
   295  		if err := config.VolcanoClient.SchedulingV1beta1().Queues().Delete(context.TODO(), openStateForDelete.Name, metav1.DeleteOptions{}); err != nil {
   296  			fmt.Printf("Delete queue with open state failed for %v.\n", err)
   297  		}
   298  		if err := config.VolcanoClient.SchedulingV1beta1().Queues().Delete(context.TODO(), closedStateForDelete.Name, metav1.DeleteOptions{}); err != nil {
   299  			fmt.Printf("Delete queue with closed state failed for %v.\n", err)
   300  		}
   301  		if err := config.VolcanoClient.SchedulingV1beta1().Queues().Delete(context.TODO(), ordinaryHierchicalQueue.Name, metav1.DeleteOptions{}); err != nil {
   302  			t.Errorf("Delete hierarchical queue failed for %v.", err)
   303  		}
   304  	}()
   305  
   306  	testCases := []struct {
   307  		Name           string
   308  		AR             admissionv1.AdmissionReview
   309  		reviewResponse *admissionv1.AdmissionResponse
   310  	}{
   311  		{
   312  			Name: "Normal Case State Not Set During Creating",
   313  			AR: admissionv1.AdmissionReview{
   314  				TypeMeta: metav1.TypeMeta{
   315  					Kind:       "AdmissionReview",
   316  					APIVersion: "admission.k8s.io/v1beta1",
   317  				},
   318  				Request: &admissionv1.AdmissionRequest{
   319  					Kind: metav1.GroupVersionKind{
   320  						Group:   "scheduling.volcano.sh",
   321  						Version: "v1beta1",
   322  						Kind:    "Queue",
   323  					},
   324  					Resource: metav1.GroupVersionResource{
   325  						Group:    "scheduling.volcano.sh",
   326  						Version:  "v1beta1",
   327  						Resource: "queues",
   328  					},
   329  					Name:      "normal-case-not-set",
   330  					Operation: "CREATE",
   331  					Object: runtime.RawExtension{
   332  						Raw: stateNotSetJSON,
   333  					},
   334  				},
   335  			},
   336  			reviewResponse: &admissionv1.AdmissionResponse{
   337  				Allowed: true,
   338  			},
   339  		},
   340  		{
   341  			Name: "Normal Case Set State of Open During Creating",
   342  			AR: admissionv1.AdmissionReview{
   343  				TypeMeta: metav1.TypeMeta{
   344  					Kind:       "AdmissionReview",
   345  					APIVersion: "admission.k8s.io/v1beta1",
   346  				},
   347  				Request: &admissionv1.AdmissionRequest{
   348  					Kind: metav1.GroupVersionKind{
   349  						Group:   "scheduling.volcano.sh",
   350  						Version: "v1beta1",
   351  						Kind:    "Queue",
   352  					},
   353  					Resource: metav1.GroupVersionResource{
   354  						Group:    "scheduling.volcano.sh",
   355  						Version:  "v1beta1",
   356  						Resource: "queues",
   357  					},
   358  					Name:      "normal-case-set-open",
   359  					Operation: "CREATE",
   360  					Object: runtime.RawExtension{
   361  						Raw: openStateJSON,
   362  					},
   363  				},
   364  			},
   365  			reviewResponse: &admissionv1.AdmissionResponse{
   366  				Allowed: true,
   367  			},
   368  		},
   369  		{
   370  			Name: "Normal Case Set State of Closed During Creating",
   371  			AR: admissionv1.AdmissionReview{
   372  				TypeMeta: metav1.TypeMeta{
   373  					Kind:       "AdmissionReview",
   374  					APIVersion: "admission.k8s.io/v1beta1",
   375  				},
   376  				Request: &admissionv1.AdmissionRequest{
   377  					Kind: metav1.GroupVersionKind{
   378  						Group:   "scheduling.volcano.sh",
   379  						Version: "v1beta1",
   380  						Kind:    "Queue",
   381  					},
   382  					Resource: metav1.GroupVersionResource{
   383  						Group:    "scheduling.volcano.sh",
   384  						Version:  "v1beta1",
   385  						Resource: "queues",
   386  					},
   387  					Name:      "normal-case-set-closed",
   388  					Operation: "CREATE",
   389  					Object: runtime.RawExtension{
   390  						Raw: closedStateJSON,
   391  					},
   392  				},
   393  			},
   394  			reviewResponse: &admissionv1.AdmissionResponse{
   395  				Allowed: true,
   396  			},
   397  		},
   398  		{
   399  			Name: "Abnormal Case Wrong State Configured During Creating",
   400  			AR: admissionv1.AdmissionReview{
   401  				TypeMeta: metav1.TypeMeta{
   402  					Kind:       "AdmissionReview",
   403  					APIVersion: "admission.k8s.io/v1beta1",
   404  				},
   405  				Request: &admissionv1.AdmissionRequest{
   406  					Kind: metav1.GroupVersionKind{
   407  						Group:   "scheduling.volcano.sh",
   408  						Version: "v1beta1",
   409  						Kind:    "Queue",
   410  					},
   411  					Resource: metav1.GroupVersionResource{
   412  						Group:    "scheduling.volcano.sh",
   413  						Version:  "v1beta1",
   414  						Resource: "queues",
   415  					},
   416  					Name:      "abnormal-case",
   417  					Operation: "CREATE",
   418  					Object: runtime.RawExtension{
   419  						Raw: wrongStateJSON,
   420  					},
   421  				},
   422  			},
   423  			reviewResponse: &admissionv1.AdmissionResponse{
   424  				Allowed: false,
   425  				Result: &metav1.Status{
   426  					Message: field.Invalid(field.NewPath("requestBody").Child("spec").Child("state"),
   427  						"wrong", fmt.Sprintf("queue state must be in %v", []schedulingv1beta1.QueueState{
   428  							schedulingv1beta1.QueueStateOpen,
   429  							schedulingv1beta1.QueueStateClosed,
   430  						})).Error(),
   431  				},
   432  			},
   433  		},
   434  		{
   435  			Name: "Normal Case Changing State From Open to Closed During Updating",
   436  			AR: admissionv1.AdmissionReview{
   437  				TypeMeta: metav1.TypeMeta{
   438  					Kind:       "AdmissionReview",
   439  					APIVersion: "admission.k8s.io/v1beta1",
   440  				},
   441  				Request: &admissionv1.AdmissionRequest{
   442  					Kind: metav1.GroupVersionKind{
   443  						Group:   "scheduling.volcano.sh",
   444  						Version: "v1beta1",
   445  						Kind:    "Queue",
   446  					},
   447  					Resource: metav1.GroupVersionResource{
   448  						Group:    "scheduling.volcano.sh",
   449  						Version:  "v1beta1",
   450  						Resource: "queues",
   451  					},
   452  					Name:      "normal-case-open-to-close-updating",
   453  					Operation: "UPDATE",
   454  					OldObject: runtime.RawExtension{
   455  						Raw: openStateJSON,
   456  					},
   457  					Object: runtime.RawExtension{
   458  						Raw: closedStateJSON,
   459  					},
   460  				},
   461  			},
   462  			reviewResponse: &admissionv1.AdmissionResponse{
   463  				Allowed: true,
   464  			},
   465  		},
   466  		{
   467  			Name: "Normal Case Changing State From Closed to Open During Updating",
   468  			AR: admissionv1.AdmissionReview{
   469  				TypeMeta: metav1.TypeMeta{
   470  					Kind:       "AdmissionReview",
   471  					APIVersion: "admission.k8s.io/v1beta1",
   472  				},
   473  				Request: &admissionv1.AdmissionRequest{
   474  					Kind: metav1.GroupVersionKind{
   475  						Group:   "scheduling.volcano.sh",
   476  						Version: "v1beta1",
   477  						Kind:    "Queue",
   478  					},
   479  					Resource: metav1.GroupVersionResource{
   480  						Group:    "scheduling.volcano.sh",
   481  						Version:  "v1beta1",
   482  						Resource: "queues",
   483  					},
   484  					Name:      "normal-case-closed-to-open-updating",
   485  					Operation: "UPDATE",
   486  					OldObject: runtime.RawExtension{
   487  						Raw: closedStateJSON,
   488  					},
   489  					Object: runtime.RawExtension{
   490  						Raw: openStateJSON,
   491  					},
   492  				},
   493  			},
   494  			reviewResponse: &admissionv1.AdmissionResponse{
   495  				Allowed: true,
   496  			},
   497  		},
   498  		{
   499  			Name: "Abnormal Case Changing State From Open to Wrong State During Updating",
   500  			AR: admissionv1.AdmissionReview{
   501  				TypeMeta: metav1.TypeMeta{
   502  					Kind:       "AdmissionReview",
   503  					APIVersion: "admission.k8s.io/v1beta1",
   504  				},
   505  				Request: &admissionv1.AdmissionRequest{
   506  					Kind: metav1.GroupVersionKind{
   507  						Group:   "scheduling.volcano.sh",
   508  						Version: "v1beta1",
   509  						Kind:    "Queue",
   510  					},
   511  					Resource: metav1.GroupVersionResource{
   512  						Group:    "scheduling.volcano.sh",
   513  						Version:  "v1beta1",
   514  						Resource: "queues",
   515  					},
   516  					Name:      "abnormal-case-open-to-wrong-state-updating",
   517  					Operation: "UPDATE",
   518  					OldObject: runtime.RawExtension{
   519  						Raw: openStateJSON,
   520  					},
   521  					Object: runtime.RawExtension{
   522  						Raw: wrongStateJSON,
   523  					},
   524  				},
   525  			},
   526  			reviewResponse: &admissionv1.AdmissionResponse{
   527  				Allowed: false,
   528  				Result: &metav1.Status{
   529  					Message: field.Invalid(field.NewPath("requestBody").Child("spec").Child("state"),
   530  						"wrong", fmt.Sprintf("queue state must be in %v", []schedulingv1beta1.QueueState{
   531  							schedulingv1beta1.QueueStateOpen,
   532  							schedulingv1beta1.QueueStateClosed,
   533  						})).Error(),
   534  				},
   535  			},
   536  		},
   537  		{
   538  			Name: "Normal Case Queue With Closed State Can Be Deleted",
   539  			AR: admissionv1.AdmissionReview{
   540  				TypeMeta: metav1.TypeMeta{
   541  					Kind:       "AdmissionReview",
   542  					APIVersion: "admission.k8s.io/v1beta1",
   543  				},
   544  				Request: &admissionv1.AdmissionRequest{
   545  					Kind: metav1.GroupVersionKind{
   546  						Group:   "scheduling.volcano.sh",
   547  						Version: "v1beta1",
   548  						Kind:    "Queue",
   549  					},
   550  					Resource: metav1.GroupVersionResource{
   551  						Group:    "scheduling.volcano.sh",
   552  						Version:  "v1beta1",
   553  						Resource: "queues",
   554  					},
   555  					Name:      "closed-state-for-delete",
   556  					Operation: "DELETE",
   557  					Object: runtime.RawExtension{
   558  						Raw: closedStateForDeleteJSON,
   559  					},
   560  				},
   561  			},
   562  			reviewResponse: &admissionv1.AdmissionResponse{
   563  				Allowed: true,
   564  			},
   565  		},
   566  		{
   567  			Name: "Normal Case Queue With Open State Can Be Deleted (Until close queue in kubectl supported)",
   568  			AR: admissionv1.AdmissionReview{
   569  				TypeMeta: metav1.TypeMeta{
   570  					Kind:       "AdmissionReview",
   571  					APIVersion: "admission.k8s.io/v1beta1",
   572  				},
   573  				Request: &admissionv1.AdmissionRequest{
   574  					Kind: metav1.GroupVersionKind{
   575  						Group:   "scheduling.volcano.sh",
   576  						Version: "v1beta1",
   577  						Kind:    "Queue",
   578  					},
   579  					Resource: metav1.GroupVersionResource{
   580  						Group:    "scheduling.volcano.sh",
   581  						Version:  "v1beta1",
   582  						Resource: "queues",
   583  					},
   584  					Name:      "open-state-for-delete",
   585  					Operation: "DELETE",
   586  					Object: runtime.RawExtension{
   587  						Raw: openStateForDeleteJSON,
   588  					},
   589  				},
   590  			},
   591  			reviewResponse: &admissionv1.AdmissionResponse{
   592  				Allowed: true,
   593  			},
   594  		},
   595  		{
   596  			Name: "Abnormal Case default Queue Can Not Be Deleted",
   597  			AR: admissionv1.AdmissionReview{
   598  				TypeMeta: metav1.TypeMeta{
   599  					Kind:       "AdmissionReview",
   600  					APIVersion: "admission.k8s.io/v1beta1",
   601  				},
   602  				Request: &admissionv1.AdmissionRequest{
   603  					Kind: metav1.GroupVersionKind{
   604  						Group:   "scheduling.volcano.sh",
   605  						Version: "v1beta1",
   606  						Kind:    "Queue",
   607  					},
   608  					Resource: metav1.GroupVersionResource{
   609  						Group:    "scheduling.volcano.sh",
   610  						Version:  "v1beta1",
   611  						Resource: "queues",
   612  					},
   613  					Name:      "default",
   614  					Operation: "DELETE",
   615  					Object: runtime.RawExtension{
   616  						Raw: openStateForDeleteJSON,
   617  					},
   618  				},
   619  			},
   620  			reviewResponse: &admissionv1.AdmissionResponse{
   621  				Allowed: false,
   622  				Result: &metav1.Status{
   623  					Message: fmt.Sprintf("`%s` queue can not be deleted", "default"),
   624  				},
   625  			},
   626  		},
   627  		{
   628  			Name: "Invalid Action",
   629  			AR: admissionv1.AdmissionReview{
   630  				TypeMeta: metav1.TypeMeta{
   631  					Kind:       "AdmissionReview",
   632  					APIVersion: "admission.k8s.io/v1beta1",
   633  				},
   634  				Request: &admissionv1.AdmissionRequest{
   635  					Kind: metav1.GroupVersionKind{
   636  						Group:   "scheduling.volcano.sh",
   637  						Version: "v1beta1",
   638  						Kind:    "Queue",
   639  					},
   640  					Resource: metav1.GroupVersionResource{
   641  						Group:    "scheduling.volcano.sh",
   642  						Version:  "v1beta1",
   643  						Resource: "queues",
   644  					},
   645  					Name:      "default",
   646  					Operation: "Invalid",
   647  					Object: runtime.RawExtension{
   648  						Raw: openStateForDeleteJSON,
   649  					},
   650  				},
   651  			},
   652  			reviewResponse: util.ToAdmissionResponse(fmt.Errorf("invalid operation `%s`, "+
   653  				"expect operation to be `CREATE`, `UPDATE` or `DELETE`", "Invalid")),
   654  		},
   655  		{
   656  			Name: "Create queue without weight",
   657  			AR: admissionv1.AdmissionReview{
   658  				TypeMeta: metav1.TypeMeta{
   659  					Kind:       "AdmissionReview",
   660  					APIVersion: "admission.k8s.io/v1beta1",
   661  				},
   662  				Request: &admissionv1.AdmissionRequest{
   663  					Kind: metav1.GroupVersionKind{
   664  						Group:   "scheduling.volcano.sh",
   665  						Version: "v1beta1",
   666  						Kind:    "Queue",
   667  					},
   668  					Resource: metav1.GroupVersionResource{
   669  						Group:    "scheduling.volcano.sh",
   670  						Version:  "v1beta1",
   671  						Resource: "queues",
   672  					},
   673  					Name:      "weight-not-set",
   674  					Operation: "CREATE",
   675  					Object: runtime.RawExtension{
   676  						Raw: weightNotSetJSON,
   677  					},
   678  				},
   679  			},
   680  			reviewResponse: &admissionv1.AdmissionResponse{
   681  				Allowed: false,
   682  				Result: &metav1.Status{
   683  					Message: field.Invalid(field.NewPath("requestBody").Child("spec").Child("weight"),
   684  						0, "queue weight must be a positive integer").Error(),
   685  				},
   686  			},
   687  		},
   688  		{
   689  			Name: "Create queue with negative weight",
   690  			AR: admissionv1.AdmissionReview{
   691  				TypeMeta: metav1.TypeMeta{
   692  					Kind:       "AdmissionReview",
   693  					APIVersion: "admission.k8s.io/v1beta1",
   694  				},
   695  				Request: &admissionv1.AdmissionRequest{
   696  					Kind: metav1.GroupVersionKind{
   697  						Group:   "scheduling.volcano.sh",
   698  						Version: "v1beta1",
   699  						Kind:    "Queue",
   700  					},
   701  					Resource: metav1.GroupVersionResource{
   702  						Group:    "scheduling.volcano.sh",
   703  						Version:  "v1beta1",
   704  						Resource: "queues",
   705  					},
   706  					Name:      "negative-weight",
   707  					Operation: "CREATE",
   708  					Object: runtime.RawExtension{
   709  						Raw: negativeWeightJSON,
   710  					},
   711  				},
   712  			},
   713  			reviewResponse: &admissionv1.AdmissionResponse{
   714  				Allowed: false,
   715  				Result: &metav1.Status{
   716  					Message: field.Invalid(field.NewPath("requestBody").Child("spec").Child("weight"),
   717  						-1, "queue weight must be a positive integer").Error(),
   718  				},
   719  			},
   720  		},
   721  		{
   722  			Name: "Update queue with negative weight",
   723  			AR: admissionv1.AdmissionReview{
   724  				TypeMeta: metav1.TypeMeta{
   725  					Kind:       "AdmissionReview",
   726  					APIVersion: "admission.k8s.io/v1beta1",
   727  				},
   728  				Request: &admissionv1.AdmissionRequest{
   729  					Kind: metav1.GroupVersionKind{
   730  						Group:   "scheduling.volcano.sh",
   731  						Version: "v1beta1",
   732  						Kind:    "Queue",
   733  					},
   734  					Resource: metav1.GroupVersionResource{
   735  						Group:    "scheduling.volcano.sh",
   736  						Version:  "v1beta1",
   737  						Resource: "queues",
   738  					},
   739  					Name:      "positive-weight-for-update",
   740  					Operation: "UPDATE",
   741  					OldObject: runtime.RawExtension{
   742  						Raw: positiveWeightForUpdateJSON,
   743  					},
   744  					Object: runtime.RawExtension{
   745  						Raw: negativeWeightForUpdateJSON,
   746  					},
   747  				},
   748  			},
   749  			reviewResponse: &admissionv1.AdmissionResponse{
   750  				Allowed: false,
   751  				Result: &metav1.Status{
   752  					Message: field.Invalid(field.NewPath("requestBody").Child("spec").Child("weight"),
   753  						-1, "queue weight must be a positive integer").Error(),
   754  				},
   755  			},
   756  		},
   757  
   758  		{
   759  			Name: "Abnormal Case Hierarchy And Weights Do Not Match",
   760  			AR: admissionv1.AdmissionReview{
   761  				TypeMeta: metav1.TypeMeta{
   762  					Kind:       "AdmissionReview",
   763  					APIVersion: "admission.k8s.io/v1beta1",
   764  				},
   765  				Request: &admissionv1.AdmissionRequest{
   766  					Kind: metav1.GroupVersionKind{
   767  						Group:   "scheduling.volcano.sh",
   768  						Version: "v1beta1",
   769  						Kind:    "Queue",
   770  					},
   771  					Resource: metav1.GroupVersionResource{
   772  						Group:    "scheduling.volcano.sh",
   773  						Version:  "v1beta1",
   774  						Resource: "queues",
   775  					},
   776  					Name:      "default",
   777  					Operation: "CREATE",
   778  					Object: runtime.RawExtension{
   779  						Raw: hierarchyWeightsDontMatchJSON,
   780  					},
   781  				},
   782  			},
   783  			reviewResponse: &admissionv1.AdmissionResponse{
   784  				Allowed: false,
   785  				Result: &metav1.Status{
   786  					Message: field.Invalid(field.NewPath("requestBody").Child("metadata").Child("annotations"),
   787  						"root/a/b", fmt.Sprintf("%s must have the same length with %s",
   788  							schedulingv1beta1.KubeHierarchyAnnotationKey,
   789  							schedulingv1beta1.KubeHierarchyWeightAnnotationKey,
   790  						)).Error(),
   791  				},
   792  			},
   793  		},
   794  		{
   795  			Name: "Abnormal Case Weights Is Negative",
   796  			AR: admissionv1.AdmissionReview{
   797  				TypeMeta: metav1.TypeMeta{
   798  					Kind:       "AdmissionReview",
   799  					APIVersion: "admission.k8s.io/v1beta1",
   800  				},
   801  				Request: &admissionv1.AdmissionRequest{
   802  					Kind: metav1.GroupVersionKind{
   803  						Group:   "scheduling.volcano.sh",
   804  						Version: "v1beta1",
   805  						Kind:    "Queue",
   806  					},
   807  					Resource: metav1.GroupVersionResource{
   808  						Group:    "scheduling.volcano.sh",
   809  						Version:  "v1beta1",
   810  						Resource: "queues",
   811  					},
   812  					Name:      "default",
   813  					Operation: "CREATE",
   814  					Object: runtime.RawExtension{
   815  						Raw: hierarchyWeightsNegativeJSON,
   816  					},
   817  				},
   818  			},
   819  			reviewResponse: &admissionv1.AdmissionResponse{
   820  				Allowed: false,
   821  				Result: &metav1.Status{
   822  					Message: field.Invalid(field.NewPath("requestBody").Child("metadata").Child("annotations"),
   823  						"1/-1/3",
   824  						fmt.Sprintf("%s in the %s must be larger than 0",
   825  							"-1", "1/-1/3",
   826  						)).Error(),
   827  				},
   828  			},
   829  		},
   830  		{
   831  			Name: "Abnormal Case Weights Is Format Illegal",
   832  			AR: admissionv1.AdmissionReview{
   833  				TypeMeta: metav1.TypeMeta{
   834  					Kind:       "AdmissionReview",
   835  					APIVersion: "admission.k8s.io/v1beta1",
   836  				},
   837  				Request: &admissionv1.AdmissionRequest{
   838  					Kind: metav1.GroupVersionKind{
   839  						Group:   "scheduling.volcano.sh",
   840  						Version: "v1beta1",
   841  						Kind:    "Queue",
   842  					},
   843  					Resource: metav1.GroupVersionResource{
   844  						Group:    "scheduling.volcano.sh",
   845  						Version:  "v1beta1",
   846  						Resource: "queues",
   847  					},
   848  					Name:      "default",
   849  					Operation: "CREATE",
   850  					Object: runtime.RawExtension{
   851  						Raw: weightsFormatIllegalJSON,
   852  					},
   853  				},
   854  			},
   855  			reviewResponse: &admissionv1.AdmissionResponse{
   856  				Allowed: false,
   857  				Result: &metav1.Status{
   858  					Message: field.Invalid(field.NewPath("requestBody").Child("metadata").Child("annotations"),
   859  						"1/a/3",
   860  						fmt.Sprintf("%s in the %s is invalid number: strconv.ParseFloat: parsing \"a\": invalid syntax",
   861  							"a", "1/a/3",
   862  						)).Error(),
   863  				},
   864  			},
   865  		},
   866  		{
   867  			Name: "Abnormal Case Hierarchy Is In Sub Path of Another Queue",
   868  			AR: admissionv1.AdmissionReview{
   869  				TypeMeta: metav1.TypeMeta{
   870  					Kind:       "AdmissionReview",
   871  					APIVersion: "admission.k8s.io/v1beta1",
   872  				},
   873  				Request: &admissionv1.AdmissionRequest{
   874  					Kind: metav1.GroupVersionKind{
   875  						Group:   "scheduling.volcano.sh",
   876  						Version: "v1beta1",
   877  						Kind:    "Queue",
   878  					},
   879  					Resource: metav1.GroupVersionResource{
   880  						Group:    "scheduling.volcano.sh",
   881  						Version:  "v1beta1",
   882  						Resource: "queues",
   883  					},
   884  					Name:      "default",
   885  					Operation: "CREATE",
   886  					Object: runtime.RawExtension{
   887  						Raw: hierarchicalQueueInSubPathOfAnotherQueueJSON,
   888  					},
   889  				},
   890  			},
   891  			reviewResponse: &admissionv1.AdmissionResponse{
   892  				Allowed: false,
   893  				Result: &metav1.Status{
   894  					Message: field.Invalid(field.NewPath("requestBody").Child("metadata").Child("annotations"),
   895  						"root/node1",
   896  						fmt.Sprintf("%s is not allowed to be in the sub path of %s of queue %s",
   897  							"root/node1", "root/node1/node2", ordinaryHierchicalQueue.Name,
   898  						)).Error(),
   899  				},
   900  			},
   901  		},
   902  	}
   903  
   904  	for _, testCase := range testCases {
   905  		t.Run(testCase.Name, func(t *testing.T) {
   906  			reviewResponse := AdmitQueues(testCase.AR)
   907  			if !reflect.DeepEqual(reviewResponse, testCase.reviewResponse) {
   908  				t.Errorf("Test case %s failed, expect %v, got %v", testCase.Name,
   909  					testCase.reviewResponse, reviewResponse)
   910  			}
   911  		})
   912  	}
   913  }