...

Source file src/sigs.k8s.io/gateway-api/pkg/admission/server.go

Documentation: sigs.k8s.io/gateway-api/pkg/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  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  
    25  	admission "k8s.io/api/admission/v1"
    26  	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/serializer"
    29  	"k8s.io/apimachinery/pkg/util/validation/field"
    30  	"k8s.io/klog/v2"
    31  
    32  	v1 "sigs.k8s.io/gateway-api/apis/v1"
    33  	v1Validation "sigs.k8s.io/gateway-api/apis/v1/validation"
    34  	v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
    35  	v1a2Validation "sigs.k8s.io/gateway-api/apis/v1alpha2/validation"
    36  	v1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
    37  	v1b1Validation "sigs.k8s.io/gateway-api/apis/v1beta1/validation"
    38  )
    39  
    40  const admissionReview = "AdmissionReview"
    41  
    42  var (
    43  	scheme = runtime.NewScheme()
    44  	codecs = serializer.NewCodecFactory(scheme)
    45  )
    46  
    47  var (
    48  	v1a2TCPRouteGVP = meta.GroupVersionResource{
    49  		Group:    v1alpha2.GroupVersion.Group,
    50  		Version:  v1alpha2.GroupVersion.Version,
    51  		Resource: "tcproutes",
    52  	}
    53  	v1a2UDPRouteGVP = meta.GroupVersionResource{
    54  		Group:    v1alpha2.GroupVersion.Group,
    55  		Version:  v1alpha2.GroupVersion.Version,
    56  		Resource: "udproutes",
    57  	}
    58  	v1a2TLSRouteGVP = meta.GroupVersionResource{
    59  		Group:    v1alpha2.GroupVersion.Group,
    60  		Version:  v1alpha2.GroupVersion.Version,
    61  		Resource: "tlsroutes",
    62  	}
    63  	v1a2GRPCRouteGVR = meta.GroupVersionResource{
    64  		Group:    v1alpha2.SchemeGroupVersion.Group,
    65  		Version:  v1alpha2.SchemeGroupVersion.Version,
    66  		Resource: "grpcroutes",
    67  	}
    68  	v1b1HTTPRouteGVR = meta.GroupVersionResource{
    69  		Group:    v1beta1.SchemeGroupVersion.Group,
    70  		Version:  v1beta1.SchemeGroupVersion.Version,
    71  		Resource: "httproutes",
    72  	}
    73  	v1b1GatewayGVR = meta.GroupVersionResource{
    74  		Group:    v1beta1.SchemeGroupVersion.Group,
    75  		Version:  v1beta1.SchemeGroupVersion.Version,
    76  		Resource: "gateways",
    77  	}
    78  	v1b1GatewayClassGVR = meta.GroupVersionResource{
    79  		Group:    v1beta1.SchemeGroupVersion.Group,
    80  		Version:  v1beta1.SchemeGroupVersion.Version,
    81  		Resource: "gatewayclasses",
    82  	}
    83  	v1HTTPRouteGVR = meta.GroupVersionResource{
    84  		Group:    v1.SchemeGroupVersion.Group,
    85  		Version:  v1.SchemeGroupVersion.Version,
    86  		Resource: "httproutes",
    87  	}
    88  	v1GatewayGVR = meta.GroupVersionResource{
    89  		Group:    v1.SchemeGroupVersion.Group,
    90  		Version:  v1.SchemeGroupVersion.Version,
    91  		Resource: "gateways",
    92  	}
    93  	v1GatewayClassGVR = meta.GroupVersionResource{
    94  		Group:    v1.SchemeGroupVersion.Group,
    95  		Version:  v1.SchemeGroupVersion.Version,
    96  		Resource: "gatewayclasses",
    97  	}
    98  )
    99  
   100  func log500(w http.ResponseWriter, err error) {
   101  	klog.Errorf("failed to process request: %v\n", err)
   102  	http.Error(w, err.Error(), http.StatusInternalServerError)
   103  }
   104  
   105  // ServeHTTP parses AdmissionReview requests and responds back
   106  // with the validation result of the entity.
   107  func ServeHTTP(w http.ResponseWriter, r *http.Request) {
   108  	if r.Method != http.MethodPost {
   109  		w.WriteHeader(http.StatusMethodNotAllowed)
   110  		http.Error(w, fmt.Sprintf("invalid method %s, only POST requests are allowed", r.Method), http.StatusMethodNotAllowed)
   111  		return
   112  	}
   113  
   114  	if r.Body == nil {
   115  		http.Error(w, "admission review object is missing",
   116  			http.StatusBadRequest)
   117  		return
   118  	}
   119  	data, err := io.ReadAll(r.Body)
   120  	if err != nil {
   121  		log500(w, err)
   122  		return
   123  	}
   124  
   125  	review := admission.AdmissionReview{}
   126  	err = json.Unmarshal(data, &review)
   127  	if err != nil {
   128  		http.Error(w, err.Error(), http.StatusBadRequest)
   129  		return
   130  	}
   131  
   132  	if review.Kind != admissionReview {
   133  		http.Error(w, "submitted object is not of kind AdmissionReview", http.StatusBadRequest)
   134  		return
   135  	}
   136  
   137  	response, err := handleValidation(*review.Request)
   138  	if err != nil {
   139  		log500(w, err)
   140  		return
   141  	}
   142  	review.Response = response
   143  	data, err = json.Marshal(review)
   144  	if err != nil {
   145  		log500(w, err)
   146  		return
   147  	}
   148  	_, err = w.Write(data)
   149  	if err != nil {
   150  		klog.Errorf("failed to write HTTP response: %v\n", err)
   151  		return
   152  	}
   153  }
   154  
   155  func handleValidation(request admission.AdmissionRequest) (*admission.AdmissionResponse, error) {
   156  	var (
   157  		response     admission.AdmissionResponse
   158  		deserializer = codecs.UniversalDeserializer()
   159  		fieldErr     field.ErrorList
   160  	)
   161  
   162  	if request.Operation == admission.Delete ||
   163  		request.Operation == admission.Connect {
   164  		response.UID = request.UID
   165  		response.Allowed = true
   166  		return &response, nil
   167  	}
   168  
   169  	switch request.Resource {
   170  	case v1a2TCPRouteGVP:
   171  		var tRoute v1alpha2.TCPRoute
   172  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &tRoute)
   173  		if err != nil {
   174  			return nil, err
   175  		}
   176  		fieldErr = v1a2Validation.ValidateTCPRoute(&tRoute)
   177  	case v1a2UDPRouteGVP:
   178  		var uRoute v1alpha2.UDPRoute
   179  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &uRoute)
   180  		if err != nil {
   181  			return nil, err
   182  		}
   183  		fieldErr = v1a2Validation.ValidateUDPRoute(&uRoute)
   184  	case v1a2TLSRouteGVP:
   185  		var tRoute v1alpha2.TLSRoute
   186  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &tRoute)
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  		fieldErr = v1a2Validation.ValidateTLSRoute(&tRoute)
   191  	case v1a2GRPCRouteGVR:
   192  		var gRoute v1alpha2.GRPCRoute
   193  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &gRoute)
   194  		if err != nil {
   195  			return nil, err
   196  		}
   197  
   198  		fieldErr = v1a2Validation.ValidateGRPCRoute(&gRoute)
   199  	case v1b1HTTPRouteGVR:
   200  		var hRoute v1beta1.HTTPRoute
   201  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &hRoute)
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  
   206  		fieldErr = v1b1Validation.ValidateHTTPRoute(&hRoute)
   207  	case v1b1GatewayGVR:
   208  		var gateway v1beta1.Gateway
   209  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &gateway)
   210  		if err != nil {
   211  			return nil, err
   212  		}
   213  		fieldErr = v1b1Validation.ValidateGateway(&gateway)
   214  	case v1b1GatewayClassGVR:
   215  		// runs only for updates
   216  		if request.Operation != admission.Update {
   217  			break
   218  		}
   219  		var gatewayClass v1beta1.GatewayClass
   220  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &gatewayClass)
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  		var gatewayClassOld v1beta1.GatewayClass
   225  		_, _, err = deserializer.Decode(request.OldObject.Raw, nil, &gatewayClassOld)
   226  		if err != nil {
   227  			return nil, err
   228  		}
   229  		fieldErr = v1b1Validation.ValidateGatewayClassUpdate(&gatewayClassOld, &gatewayClass)
   230  	case v1HTTPRouteGVR:
   231  		var hRoute v1.HTTPRoute
   232  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &hRoute)
   233  		if err != nil {
   234  			return nil, err
   235  		}
   236  		fieldErr = v1Validation.ValidateHTTPRoute(&hRoute)
   237  	case v1GatewayGVR:
   238  		var gateway v1.Gateway
   239  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &gateway)
   240  		if err != nil {
   241  			return nil, err
   242  		}
   243  		fieldErr = v1Validation.ValidateGateway(&gateway)
   244  	case v1GatewayClassGVR:
   245  		// runs only for updates
   246  		if request.Operation != admission.Update {
   247  			break
   248  		}
   249  		var gatewayClass v1.GatewayClass
   250  		_, _, err := deserializer.Decode(request.Object.Raw, nil, &gatewayClass)
   251  		if err != nil {
   252  			return nil, err
   253  		}
   254  		var gatewayClassOld v1.GatewayClass
   255  		_, _, err = deserializer.Decode(request.OldObject.Raw, nil, &gatewayClassOld)
   256  		if err != nil {
   257  			return nil, err
   258  		}
   259  		fieldErr = v1Validation.ValidateGatewayClassUpdate(&gatewayClassOld, &gatewayClass)
   260  	default:
   261  		return nil, fmt.Errorf("unknown resource '%v'", request.Resource.Resource)
   262  	}
   263  
   264  	if len(fieldErr) > 0 {
   265  		return &admission.AdmissionResponse{
   266  			UID:     request.UID,
   267  			Allowed: false,
   268  			Result: &meta.Status{
   269  				Message: fmt.Sprintf("%s", fieldErr.ToAggregate()),
   270  				Code:    400,
   271  			},
   272  		}, nil
   273  	}
   274  
   275  	return &admission.AdmissionResponse{
   276  		UID:     request.UID,
   277  		Allowed: true,
   278  		Result:  &meta.Status{},
   279  	}, nil
   280  }
   281  

View as plain text