127 lines
3.0 KiB
Go

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
}