github.com/oam-dev/kubevela@v1.9.11/pkg/webhook/core.oam.dev/v1beta1/application/validating_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 application
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  
    24  	admissionv1 "k8s.io/api/admission/v1"
    25  	"k8s.io/apimachinery/pkg/util/validation/field"
    26  	"sigs.k8s.io/controller-runtime/pkg/client"
    27  	"sigs.k8s.io/controller-runtime/pkg/manager"
    28  	"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
    29  	"sigs.k8s.io/controller-runtime/pkg/webhook"
    30  	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
    31  
    32  	"github.com/kubevela/workflow/pkg/cue/packages"
    33  
    34  	"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
    35  	controller "github.com/oam-dev/kubevela/pkg/controller/core.oam.dev"
    36  	"github.com/oam-dev/kubevela/pkg/oam/util"
    37  )
    38  
    39  var _ admission.Handler = &ValidatingHandler{}
    40  
    41  // ValidatingHandler handles application
    42  type ValidatingHandler struct {
    43  	pd     *packages.PackageDiscover
    44  	Client client.Client
    45  	// Decoder decodes objects
    46  	Decoder *admission.Decoder
    47  }
    48  
    49  var _ inject.Client = &ValidatingHandler{}
    50  
    51  // InjectClient injects the client into the ApplicationValidateHandler
    52  func (h *ValidatingHandler) InjectClient(c client.Client) error {
    53  	if h.Client != nil {
    54  		return nil
    55  	}
    56  	h.Client = c
    57  	return nil
    58  }
    59  
    60  var _ admission.DecoderInjector = &ValidatingHandler{}
    61  
    62  // InjectDecoder injects the decoder into the ApplicationValidateHandler
    63  func (h *ValidatingHandler) InjectDecoder(d *admission.Decoder) error {
    64  	if h.Decoder != nil {
    65  		return nil
    66  	}
    67  	h.Decoder = d
    68  	return nil
    69  }
    70  
    71  func simplifyError(err error) error {
    72  	switch e := err.(type) { // nolint
    73  	case *field.Error:
    74  		return fmt.Errorf("field \"%s\": %s error encountered, %s. ", e.Field, e.Type, e.Detail)
    75  	default:
    76  		return err
    77  	}
    78  }
    79  
    80  func mergeErrors(errs field.ErrorList) error {
    81  	s := ""
    82  	for _, err := range errs {
    83  		s += fmt.Sprintf("field \"%s\": %s error encountered, %s. ", err.Field, err.Type, err.Detail)
    84  	}
    85  	return fmt.Errorf(s)
    86  }
    87  
    88  // Handle validate Application Spec here
    89  func (h *ValidatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
    90  	app := &v1beta1.Application{}
    91  	if err := h.Decoder.Decode(req, app); err != nil {
    92  		return admission.Errored(http.StatusBadRequest, err)
    93  	}
    94  	ctx = util.SetNamespaceInCtx(ctx, app.Namespace)
    95  	switch req.Operation {
    96  	case admissionv1.Create:
    97  		if allErrs := h.ValidateCreate(ctx, app); len(allErrs) > 0 {
    98  			// http.StatusUnprocessableEntity will NOT report any error descriptions
    99  			// to the client, use generic http.StatusBadRequest instead.
   100  			return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
   101  		}
   102  	case admissionv1.Update:
   103  		oldApp := &v1beta1.Application{}
   104  		if err := h.Decoder.DecodeRaw(req.AdmissionRequest.OldObject, oldApp); err != nil {
   105  			return admission.Errored(http.StatusBadRequest, simplifyError(err))
   106  		}
   107  		if app.ObjectMeta.DeletionTimestamp.IsZero() {
   108  			if allErrs := h.ValidateUpdate(ctx, app, oldApp); len(allErrs) > 0 {
   109  				return admission.Errored(http.StatusBadRequest, mergeErrors(allErrs))
   110  			}
   111  		}
   112  	default:
   113  		// Do nothing for DELETE and CONNECT
   114  	}
   115  	return admission.ValidationResponse(true, "")
   116  }
   117  
   118  // RegisterValidatingHandler will register application validate handler to the webhook
   119  func RegisterValidatingHandler(mgr manager.Manager, args controller.Args) {
   120  	server := mgr.GetWebhookServer()
   121  	server.Register("/validating-core-oam-dev-v1beta1-applications", &webhook.Admission{Handler: &ValidatingHandler{pd: args.PackageDiscover}})
   122  }