github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/cloud/pkg/admissioncontroller/admit_devicemodel.go (about)

     1  package admissioncontroller
     2  
     3  import (
     4  	"encoding/json"
     5  	"io/ioutil"
     6  	"net/http"
     7  	"strings"
     8  
     9  	admissionv1beta1 "k8s.io/api/admission/v1beta1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/klog"
    12  
    13  	devicesv1alpha1 "github.com/kubeedge/kubeedge/cloud/pkg/apis/devices/v1alpha1"
    14  )
    15  
    16  // admitFunc is the type we use for all of our validators and mutators
    17  type admitFunc func(admissionv1beta1.AdmissionReview) *admissionv1beta1.AdmissionResponse
    18  
    19  func serve(w http.ResponseWriter, r *http.Request, admit admitFunc) {
    20  	var body []byte
    21  	if r.Body != nil {
    22  		if data, err := ioutil.ReadAll(r.Body); err == nil {
    23  			body = data
    24  		}
    25  	}
    26  
    27  	// verify the content type is accurate
    28  	contentType := r.Header.Get("Content-Type")
    29  	if contentType != "application/json" {
    30  		klog.Fatalf("contentType=%s, expect application/json", contentType)
    31  		return
    32  	}
    33  
    34  	// The AdmissionReview that was sent to the webhook
    35  	requestedAdmissionReview := admissionv1beta1.AdmissionReview{}
    36  
    37  	// The AdmissionReview that will be returned
    38  	responseAdmissionReview := admissionv1beta1.AdmissionReview{}
    39  
    40  	deserializer := codecs.UniversalDeserializer()
    41  	if _, _, err := deserializer.Decode(body, nil, &requestedAdmissionReview); err != nil {
    42  		klog.Fatalf("decode failed with error: %v", err)
    43  		responseAdmissionReview.Response = toAdmissionResponse(err)
    44  	} else {
    45  		responseAdmissionReview.Response = admit(requestedAdmissionReview)
    46  	}
    47  
    48  	// Return the same UID
    49  	responseAdmissionReview.Response.UID = requestedAdmissionReview.Request.UID
    50  	klog.Infof("sending response: %v", responseAdmissionReview.Response)
    51  
    52  	respBytes, err := json.Marshal(responseAdmissionReview)
    53  	if err != nil {
    54  		klog.Fatalf("cannot marshal to a valid reponse %v", err)
    55  	}
    56  	if _, err := w.Write(respBytes); err != nil {
    57  		klog.Fatalf("cannot write reponse %v", err)
    58  	}
    59  }
    60  
    61  func admitDeviceModel(review admissionv1beta1.AdmissionReview) *admissionv1beta1.AdmissionResponse {
    62  	reviewResponse := admissionv1beta1.AdmissionResponse{}
    63  	reviewResponse.Allowed = true
    64  	var msg string
    65  
    66  	switch review.Request.Operation {
    67  	case admissionv1beta1.Create, admissionv1beta1.Update:
    68  		raw := review.Request.Object.Raw
    69  		devicemodel := devicesv1alpha1.DeviceModel{}
    70  		deserializer := codecs.UniversalDeserializer()
    71  		if _, _, err := deserializer.Decode(raw, nil, &devicemodel); err != nil {
    72  			klog.Errorf("validation failed with error: %v", err)
    73  			return toAdmissionResponse(err)
    74  		}
    75  		msg = validateDeviceModel(&devicemodel, &reviewResponse)
    76  	case admissionv1beta1.Delete, admissionv1beta1.Connect:
    77  		//no rule defined for above operations, greenlight for all of above.
    78  		reviewResponse.Allowed = true
    79  		klog.Info("admission validation passed!")
    80  	default:
    81  		klog.Infof("Unsupported webhook operation %v", review.Request.Operation)
    82  		reviewResponse.Allowed = false
    83  		msg = msg + "Unsupported webhook operation!"
    84  	}
    85  	if !reviewResponse.Allowed {
    86  		reviewResponse.Result = &metav1.Status{Message: strings.TrimSpace(msg)}
    87  	}
    88  	return &reviewResponse
    89  }
    90  
    91  func validateDeviceModel(devicemodel *devicesv1alpha1.DeviceModel, response *admissionv1beta1.AdmissionResponse) string {
    92  	//device properties must be either Int or String while additional properties is not banned.
    93  	var msg string
    94  	for _, property := range devicemodel.Spec.Properties {
    95  		if property.Type.String == nil && property.Type.Int == nil {
    96  			msg = "Either Int or String must be set"
    97  			response.Allowed = false
    98  		} else if property.Type.String != nil && property.Type.Int != nil {
    99  			msg = "Only one of [Int, String] could be set for properties"
   100  			response.Allowed = false
   101  		}
   102  	}
   103  	return msg
   104  }
   105  
   106  // toAdmissionResponse is a helper function to create an AdmissionResponse
   107  func toAdmissionResponse(err error) *admissionv1beta1.AdmissionResponse {
   108  	return &admissionv1beta1.AdmissionResponse{
   109  		Result: &metav1.Status{
   110  			Message: err.Error(),
   111  		},
   112  	}
   113  }
   114  
   115  func serveDeviceModel(w http.ResponseWriter, r *http.Request) {
   116  	serve(w, r, admitDeviceModel)
   117  }