volcano.sh/volcano@v1.9.0/pkg/webhooks/admission/queues/mutate/mutate_queue.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 mutate 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "strings" 23 24 admissionv1 "k8s.io/api/admission/v1" 25 whv1 "k8s.io/api/admissionregistration/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/klog/v2" 28 29 schedulingv1beta1 "volcano.sh/apis/pkg/apis/scheduling/v1beta1" 30 "volcano.sh/volcano/pkg/webhooks/router" 31 "volcano.sh/volcano/pkg/webhooks/schema" 32 "volcano.sh/volcano/pkg/webhooks/util" 33 ) 34 35 func init() { 36 router.RegisterAdmission(service) 37 } 38 39 var service = &router.AdmissionService{ 40 Path: "/queues/mutate", 41 Func: Queues, 42 43 MutatingConfig: &whv1.MutatingWebhookConfiguration{ 44 Webhooks: []whv1.MutatingWebhook{{ 45 Name: "mutatequeue.volcano.sh", 46 Rules: []whv1.RuleWithOperations{ 47 { 48 Operations: []whv1.OperationType{whv1.Create}, 49 Rule: whv1.Rule{ 50 APIGroups: []string{schedulingv1beta1.SchemeGroupVersion.Group}, 51 APIVersions: []string{schedulingv1beta1.SchemeGroupVersion.Version}, 52 Resources: []string{"queues"}, 53 }, 54 }, 55 }, 56 }}, 57 }, 58 } 59 60 type patchOperation struct { 61 Op string `json:"op"` 62 Path string `json:"path"` 63 Value interface{} `json:"value,omitempty"` 64 } 65 66 // Queues mutate queues. 67 func Queues(ar admissionv1.AdmissionReview) *admissionv1.AdmissionResponse { 68 klog.V(3).Infof("Mutating %s queue %s.", ar.Request.Operation, ar.Request.Name) 69 70 queue, err := schema.DecodeQueue(ar.Request.Object, ar.Request.Resource) 71 if err != nil { 72 return util.ToAdmissionResponse(err) 73 } 74 75 var patchBytes []byte 76 switch ar.Request.Operation { 77 case admissionv1.Create: 78 patchBytes, err = createQueuePatch(queue) 79 default: 80 return util.ToAdmissionResponse(fmt.Errorf("invalid operation `%s`, "+ 81 "expect operation to be `CREATE`", ar.Request.Operation)) 82 } 83 84 if err != nil { 85 return &admissionv1.AdmissionResponse{ 86 Allowed: false, 87 Result: &metav1.Status{Message: err.Error()}, 88 } 89 } 90 91 reviewResponse := admissionv1.AdmissionResponse{ 92 Allowed: true, 93 Patch: patchBytes, 94 } 95 if len(patchBytes) > 0 { 96 pt := admissionv1.PatchTypeJSONPatch 97 reviewResponse.PatchType = &pt 98 } 99 return &reviewResponse 100 } 101 102 func createQueuePatch(queue *schedulingv1beta1.Queue) ([]byte, error) { 103 var patch []patchOperation 104 105 // add root node if the root node not specified 106 hierarchy := queue.Annotations[schedulingv1beta1.KubeHierarchyAnnotationKey] 107 hierarchicalWeights := queue.Annotations[schedulingv1beta1.KubeHierarchyWeightAnnotationKey] 108 109 if hierarchy != "" && hierarchicalWeights != "" && !strings.HasPrefix(hierarchy, "root") { 110 // based on https://tools.ietf.org/html/rfc6901#section-3 111 // escape "/" with "~1" 112 patch = append(patch, patchOperation{ 113 Op: "add", 114 Path: fmt.Sprintf("/metadata/annotations/%s", strings.ReplaceAll(schedulingv1beta1.KubeHierarchyAnnotationKey, "/", "~1")), 115 Value: fmt.Sprintf("root/%s", hierarchy), 116 }) 117 patch = append(patch, patchOperation{ 118 Op: "add", 119 Path: fmt.Sprintf("/metadata/annotations/%s", strings.ReplaceAll(schedulingv1beta1.KubeHierarchyWeightAnnotationKey, "/", "~1")), 120 Value: fmt.Sprintf("1/%s", hierarchicalWeights), 121 }) 122 } 123 124 trueValue := true 125 if queue.Spec.Reclaimable == nil { 126 patch = append(patch, patchOperation{ 127 Op: "add", 128 Path: "/spec/reclaimable", 129 Value: &trueValue, 130 }) 131 } 132 133 defaultWeight := 1 134 if queue.Spec.Weight == 0 { 135 patch = append(patch, patchOperation{ 136 Op: "add", 137 Path: "/spec/weight", 138 Value: &defaultWeight, 139 }) 140 } 141 142 return json.Marshal(patch) 143 }