package main import ( "encoding/json" "fmt" "io" "net/http" "strings" admission "k8s.io/api/admission/v1" core "k8s.io/api/core/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" ) type result struct { Allowed bool Msg string PatchOps []patchOperation } type admitFunc func(request *admission.AdmissionRequest) (*result, error) type hook struct { Create admitFunc Delete admitFunc Update admitFunc Connect admitFunc } func webMutatePod(w http.ResponseWriter, r *http.Request) { //https://github.com/douglasmakey/admissioncontroller podsValidation := hook{ Create: validateCreate(), } admissionHandler := &struct { decoder runtime.Decoder }{ decoder: serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer(), } // read request body body, err := io.ReadAll(r.Body) if err != nil { tmpltError(w, http.StatusBadRequest, "No data in request body.") return } // see if request body can be decoded var review admission.AdmissionReview if _, _, err := admissionHandler.decoder.Decode(body, nil, &review); err != nil { tmpltError(w, http.StatusBadRequest, "Unable to decode request body.") return } var o *result switch review.Request.Operation { case admission.Create: if podsValidation.Create == nil { tmpltError(w, http.StatusBadRequest, fmt.Sprintf("operation %s is not registered", review.Request.Operation)) return } o, _ = podsValidation.Create(review.Request) case admission.Update: if podsValidation.Update == nil { tmpltError(w, http.StatusBadRequest, fmt.Sprintf("operation %s is not registered", review.Request.Operation)) return } o, _ = podsValidation.Update(review.Request) case admission.Delete: if podsValidation.Delete == nil { tmpltError(w, http.StatusBadRequest, fmt.Sprintf("operation %s is not registered", review.Request.Operation)) return } o, _ = podsValidation.Delete(review.Request) case admission.Connect: if podsValidation.Connect == nil { tmpltError(w, http.StatusBadRequest, fmt.Sprintf("operation %s is not registered", review.Request.Operation)) return } o, _ = podsValidation.Connect(review.Request) } admissionResult := admission.AdmissionReview{ Response: &admission.AdmissionResponse{ UID: review.Request.UID, Allowed: o.Allowed, Result: &meta.Status{ Message: o.Msg, }, }, } resp, _ := json.Marshal(admissionResult) w.WriteHeader(http.StatusOK) w.Write(resp) } func validateCreate() admitFunc { return func(r *admission.AdmissionRequest) (*result, error) { pod, err := parsePod(r.Object.Raw) if err != nil { return &result{Msg: err.Error()}, nil } for _, c := range pod.Spec.Containers { if strings.HasSuffix(c.Image, ":latest") { return &result{Msg: "You cannot use the tag 'latest' in a container."}, nil } } return &result{Allowed: true}, nil } } func parsePod(object []byte) (*core.Pod, error) { var pod core.Pod if err := json.Unmarshal(object, &pod); err != nil { return nil, err } return &pod, nil }