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  }