github.com/oam-dev/kubevela@v1.9.11/pkg/webhook/core.oam.dev/v1beta1/componentdefinition/mutating_handler.go (about) 1 /* 2 Copyright 2021. The KubeVela 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 componentdefinition 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "net/http" 24 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 "k8s.io/klog/v2" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 "sigs.k8s.io/controller-runtime/pkg/manager" 29 "sigs.k8s.io/controller-runtime/pkg/runtime/inject" 30 "sigs.k8s.io/controller-runtime/pkg/webhook" 31 "sigs.k8s.io/controller-runtime/pkg/webhook/admission" 32 33 "github.com/oam-dev/kubevela/apis/core.oam.dev/common" 34 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 35 "github.com/oam-dev/kubevela/apis/types" 36 controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev" 37 "github.com/oam-dev/kubevela/pkg/oam/util" 38 ) 39 40 // MutatingHandler handles ComponentDefinition 41 type MutatingHandler struct { 42 Client client.Client 43 // Decoder decodes objects 44 Decoder *admission.Decoder 45 // AutoGenWorkloadDef indicates whether create workloadDef which componentDef refers to 46 AutoGenWorkloadDef bool 47 } 48 49 var _ admission.Handler = &MutatingHandler{} 50 51 // Handle handles admission requests. 52 func (h *MutatingHandler) Handle(_ context.Context, req admission.Request) admission.Response { 53 obj := &v1beta1.ComponentDefinition{} 54 55 err := h.Decoder.Decode(req, obj) 56 if err != nil { 57 return admission.Errored(http.StatusBadRequest, err) 58 } 59 // mutate the object 60 if err := h.Mutate(obj); err != nil { 61 klog.ErrorS(err, "failed to mutate the componentDefinition", "name", obj.Name) 62 return admission.Errored(http.StatusBadRequest, err) 63 } 64 65 marshalled, err := json.Marshal(obj) 66 if err != nil { 67 return admission.Errored(http.StatusInternalServerError, err) 68 } 69 70 resp := admission.PatchResponseFromRaw(req.AdmissionRequest.Object.Raw, marshalled) 71 if len(resp.Patches) > 0 { 72 klog.InfoS("admit ComponentDefinition", 73 "namespace", obj.Namespace, "name", obj.Name, "patches", util.JSONMarshal(resp.Patches)) 74 } 75 return resp 76 } 77 78 // Mutate sets all the default value for the ComponentDefinition 79 func (h *MutatingHandler) Mutate(obj *v1beta1.ComponentDefinition) error { 80 klog.InfoS("mutate", "name", obj.Name) 81 82 // If the Type field is not empty, it means that ComponentDefinition refers to an existing WorkloadDefinition 83 if obj.Spec.Workload.Type != types.AutoDetectWorkloadDefinition && (obj.Spec.Workload.Type != "" && obj.Spec.Workload.Definition == (common.WorkloadGVK{})) { 84 workloadDef := new(v1beta1.WorkloadDefinition) 85 return h.Client.Get(context.TODO(), client.ObjectKey{Name: obj.Spec.Workload.Type, Namespace: obj.Namespace}, workloadDef) 86 } 87 88 if obj.Spec.Workload.Definition != (common.WorkloadGVK{}) { 89 // If only Definition field exists, fill Type field according to Definition. 90 defRef, err := util.ConvertWorkloadGVK2Definition(h.Client.RESTMapper(), obj.Spec.Workload.Definition) 91 if err != nil { 92 return err 93 } 94 95 if obj.Spec.Workload.Type == "" { 96 obj.Spec.Workload.Type = defRef.Name 97 } 98 99 workloadDef := new(v1beta1.WorkloadDefinition) 100 err = h.Client.Get(context.TODO(), client.ObjectKey{Name: defRef.Name, Namespace: obj.Namespace}, workloadDef) 101 if err != nil { 102 if apierrors.IsNotFound(err) { 103 // Create workloadDefinition which componentDefinition refers to 104 if h.AutoGenWorkloadDef { 105 workloadDef.SetName(defRef.Name) 106 workloadDef.SetNamespace(obj.Namespace) 107 workloadDef.Spec.Reference = defRef 108 return h.Client.Create(context.TODO(), workloadDef) 109 } 110 111 return fmt.Errorf("workloadDefinition %s referenced by componentDefinition is not found, please create the workloadDefinition first", defRef.Name) 112 } 113 return err 114 } 115 return nil 116 } 117 118 if obj.Spec.Workload.Type == "" { 119 obj.Spec.Workload.Type = types.AutoDetectWorkloadDefinition 120 } 121 return nil 122 } 123 124 var _ admission.DecoderInjector = &MutatingHandler{} 125 126 // InjectDecoder injects the decoder into the ComponentDefinitionMutatingHandler 127 func (h *MutatingHandler) InjectDecoder(d *admission.Decoder) error { 128 h.Decoder = d 129 return nil 130 } 131 132 var _ inject.Client = &MutatingHandler{} 133 134 // InjectClient injects the client into the ApplicationValidateHandler 135 func (h *MutatingHandler) InjectClient(c client.Client) error { 136 if h.Client != nil { 137 return nil 138 } 139 h.Client = c 140 return nil 141 } 142 143 // RegisterMutatingHandler will register component mutation handler to the webhook 144 func RegisterMutatingHandler(mgr manager.Manager, args controller.Args) { 145 server := mgr.GetWebhookServer() 146 server.Register("/mutating-core-oam-dev-v1beta1-componentdefinitions", &webhook.Admission{ 147 Handler: &MutatingHandler{AutoGenWorkloadDef: args.AutoGenWorkloadDefinition}, 148 }) 149 }