github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/agent/http/admission.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // SPDX-FileCopyrightText: 2021-Present The Jackal Authors
     3  
     4  // Package http provides a http server for the webhook and proxy.
     5  package http
     6  
     7  import (
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"net/http"
    12  
    13  	"github.com/Racer159/jackal/src/config/lang"
    14  	"github.com/Racer159/jackal/src/internal/agent/operations"
    15  	"github.com/Racer159/jackal/src/pkg/message"
    16  	v1 "k8s.io/api/admission/v1"
    17  	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  	"k8s.io/apimachinery/pkg/runtime"
    19  	"k8s.io/apimachinery/pkg/runtime/serializer"
    20  )
    21  
    22  // admissionHandler represents the HTTP handler for an admission webhook.
    23  type admissionHandler struct {
    24  	decoder runtime.Decoder
    25  }
    26  
    27  // newAdmissionHandler returns an instance of AdmissionHandler.
    28  func newAdmissionHandler() *admissionHandler {
    29  	return &admissionHandler{
    30  		decoder: serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer(),
    31  	}
    32  }
    33  
    34  // Serve returns a http.HandlerFunc for an admission webhook.
    35  func (h *admissionHandler) Serve(hook operations.Hook) http.HandlerFunc {
    36  	message.Debugf("http.Serve(%#v)", hook)
    37  	return func(w http.ResponseWriter, r *http.Request) {
    38  		message.Debugf("http.Serve()(writer, %#v)", r.URL)
    39  
    40  		w.Header().Set("Content-Type", "application/json")
    41  		if r.Method != http.MethodPost {
    42  			http.Error(w, lang.AgentErrInvalidMethod, http.StatusMethodNotAllowed)
    43  			return
    44  		}
    45  
    46  		if contentType := r.Header.Get("Content-Type"); contentType != "application/json" {
    47  			http.Error(w, lang.AgentErrInvalidType, http.StatusBadRequest)
    48  			return
    49  		}
    50  
    51  		body, err := io.ReadAll(r.Body)
    52  		if err != nil {
    53  			http.Error(w, fmt.Sprintf(lang.AgentErrBadRequest, err), http.StatusBadRequest)
    54  			return
    55  		}
    56  
    57  		var review v1.AdmissionReview
    58  		if _, _, err := h.decoder.Decode(body, nil, &review); err != nil {
    59  			http.Error(w, fmt.Sprintf(lang.AgentErrCouldNotDeserializeReq, err), http.StatusBadRequest)
    60  			return
    61  		}
    62  
    63  		if review.Request == nil {
    64  			http.Error(w, lang.AgentErrNilReq, http.StatusBadRequest)
    65  			return
    66  		}
    67  
    68  		result, err := hook.Execute(review.Request)
    69  		if err != nil {
    70  			message.WarnErr(err, lang.AgentErrBindHandler)
    71  			w.WriteHeader(http.StatusInternalServerError)
    72  			return
    73  		}
    74  
    75  		admissionResponse := v1.AdmissionReview{
    76  			TypeMeta: meta.TypeMeta{
    77  				APIVersion: v1.SchemeGroupVersion.String(),
    78  				Kind:       "AdmissionReview",
    79  			},
    80  			Response: &v1.AdmissionResponse{
    81  				UID:     review.Request.UID,
    82  				Allowed: result.Allowed,
    83  				Result:  &meta.Status{Message: result.Msg},
    84  			},
    85  		}
    86  
    87  		// set the patch operations for mutating admission
    88  		if len(result.PatchOps) > 0 {
    89  			jsonPatchType := v1.PatchTypeJSONPatch
    90  			patchBytes, err := json.Marshal(result.PatchOps)
    91  			if err != nil {
    92  				message.WarnErr(err, lang.AgentErrMarshallJSONPatch)
    93  				http.Error(w, lang.AgentErrMarshallJSONPatch, http.StatusInternalServerError)
    94  			}
    95  			admissionResponse.Response.Patch = patchBytes
    96  			admissionResponse.Response.PatchType = &jsonPatchType
    97  		}
    98  
    99  		jsonResponse, err := json.Marshal(admissionResponse)
   100  		if err != nil {
   101  			message.WarnErr(err, lang.AgentErrMarshalResponse)
   102  			http.Error(w, lang.AgentErrMarshalResponse, http.StatusInternalServerError)
   103  			return
   104  		}
   105  
   106  		message.Debug("PATCH: ", string(admissionResponse.Response.Patch))
   107  		message.Debug("RESPONSE: ", string(jsonResponse))
   108  
   109  		message.Infof(lang.AgentInfoWebhookAllowed, r.URL.Path, review.Request.Operation, result.Allowed)
   110  		w.WriteHeader(http.StatusOK)
   111  		w.Write(jsonResponse)
   112  	}
   113  }