...

Source file src/sigs.k8s.io/controller-runtime/pkg/webhook/admission/validator_custom.go

Documentation: sigs.k8s.io/controller-runtime/pkg/webhook/admission

     1  /*
     2  Copyright 2021 The Kubernetes 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 admission
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"net/http"
    24  
    25  	v1 "k8s.io/api/admission/v1"
    26  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  )
    29  
    30  // CustomValidator defines functions for validating an operation.
    31  // The object to be validated is passed into methods as a parameter.
    32  type CustomValidator interface {
    33  	// ValidateCreate validates the object on creation.
    34  	// The optional warnings will be added to the response as warning messages.
    35  	// Return an error if the object is invalid.
    36  	ValidateCreate(ctx context.Context, obj runtime.Object) (warnings Warnings, err error)
    37  
    38  	// ValidateUpdate validates the object on update.
    39  	// The optional warnings will be added to the response as warning messages.
    40  	// Return an error if the object is invalid.
    41  	ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings Warnings, err error)
    42  
    43  	// ValidateDelete validates the object on deletion.
    44  	// The optional warnings will be added to the response as warning messages.
    45  	// Return an error if the object is invalid.
    46  	ValidateDelete(ctx context.Context, obj runtime.Object) (warnings Warnings, err error)
    47  }
    48  
    49  // WithCustomValidator creates a new Webhook for validating the provided type.
    50  func WithCustomValidator(scheme *runtime.Scheme, obj runtime.Object, validator CustomValidator) *Webhook {
    51  	return &Webhook{
    52  		Handler: &validatorForType{object: obj, validator: validator, decoder: NewDecoder(scheme)},
    53  	}
    54  }
    55  
    56  type validatorForType struct {
    57  	validator CustomValidator
    58  	object    runtime.Object
    59  	decoder   Decoder
    60  }
    61  
    62  // Handle handles admission requests.
    63  func (h *validatorForType) Handle(ctx context.Context, req Request) Response {
    64  	if h.decoder == nil {
    65  		panic("decoder should never be nil")
    66  	}
    67  	if h.validator == nil {
    68  		panic("validator should never be nil")
    69  	}
    70  	if h.object == nil {
    71  		panic("object should never be nil")
    72  	}
    73  
    74  	ctx = NewContextWithRequest(ctx, req)
    75  
    76  	// Get the object in the request
    77  	obj := h.object.DeepCopyObject()
    78  
    79  	var err error
    80  	var warnings []string
    81  
    82  	switch req.Operation {
    83  	case v1.Connect:
    84  		// No validation for connect requests.
    85  		// TODO(vincepri): Should we validate CONNECT requests? In what cases?
    86  	case v1.Create:
    87  		if err := h.decoder.Decode(req, obj); err != nil {
    88  			return Errored(http.StatusBadRequest, err)
    89  		}
    90  
    91  		warnings, err = h.validator.ValidateCreate(ctx, obj)
    92  	case v1.Update:
    93  		oldObj := obj.DeepCopyObject()
    94  		if err := h.decoder.DecodeRaw(req.Object, obj); err != nil {
    95  			return Errored(http.StatusBadRequest, err)
    96  		}
    97  		if err := h.decoder.DecodeRaw(req.OldObject, oldObj); err != nil {
    98  			return Errored(http.StatusBadRequest, err)
    99  		}
   100  
   101  		warnings, err = h.validator.ValidateUpdate(ctx, oldObj, obj)
   102  	case v1.Delete:
   103  		// In reference to PR: https://github.com/kubernetes/kubernetes/pull/76346
   104  		// OldObject contains the object being deleted
   105  		if err := h.decoder.DecodeRaw(req.OldObject, obj); err != nil {
   106  			return Errored(http.StatusBadRequest, err)
   107  		}
   108  
   109  		warnings, err = h.validator.ValidateDelete(ctx, obj)
   110  	default:
   111  		return Errored(http.StatusBadRequest, fmt.Errorf("unknown operation %q", req.Operation))
   112  	}
   113  
   114  	// Check the error message first.
   115  	if err != nil {
   116  		var apiStatus apierrors.APIStatus
   117  		if errors.As(err, &apiStatus) {
   118  			return validationResponseFromStatus(false, apiStatus.Status()).WithWarnings(warnings...)
   119  		}
   120  		return Denied(err.Error()).WithWarnings(warnings...)
   121  	}
   122  
   123  	// Return allowed if everything succeeded.
   124  	return Allowed("").WithWarnings(warnings...)
   125  }
   126  

View as plain text