package main import ( "net/http" "log" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "os" "fmt" "path/filepath" "k8s.io/client-go/kubernetes" rest "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/util/homedir" "flag" "strconv" "io/ioutil" "k8s.io/api/admission/v1beta1" "errors" apiv1 "k8s.io/api/core/v1" "encoding/json" ) var ( universalDeserializer = serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer() ) var parameters ServerParameters type ServerParameters struct { port int // webhook server port certFile string // path to the x509 certificate for https keyFile string // path to the x509 private key matching `CertFile` } var config *rest.Config var clientSet *kubernetes.Clientset type patchOperation struct { Op string `json:"op"` Path string `json:"path"` Value interface{} `json:"value,omitempty"` } func main() { useKubeConfig := os.Getenv("USE_KUBECONFIG") kubeConfigFilePath := os.Getenv("KUBECONFIG") flag.IntVar(¶meters.port, "port", 8443, "Webhook server port.") flag.StringVar(¶meters.certFile, "tlsCertFile", "/etc/webhook/certs/tls.crt", "File containing the x509 Certificate for HTTPS.") flag.StringVar(¶meters.keyFile, "tlsKeyFile", "/etc/webhook/certs/tls.key", "File containing the x509 private key to --tlsCertFile.") flag.Parse() if len(useKubeConfig) == 0 { // default to service account in cluster token c, err := rest.InClusterConfig() if err != nil { panic(err.Error()) } config = c } else { //load from a kube config var kubeconfig string if kubeConfigFilePath == "" { if home := homedir.HomeDir(); home != "" { kubeconfig = filepath.Join(home, ".kube", "config") } } else { kubeconfig = kubeConfigFilePath } fmt.Println("kubeconfig: " + kubeconfig) c, err := clientcmd.BuildConfigFromFlags("", kubeconfig) if err != nil { panic(err.Error()) } config = c } cs, err := kubernetes.NewForConfig(config) if err != nil { panic(err.Error()) } clientSet = cs test() http.HandleFunc("/", HandleRoot) http.HandleFunc("/mutate", HandleMutate) log.Fatal(http.ListenAndServeTLS(":" + strconv.Itoa(parameters.port), parameters.certFile, parameters.keyFile, nil)) } func HandleRoot(w http.ResponseWriter, r *http.Request){ w.Write([]byte("HandleRoot!")) } func HandleMutate(w http.ResponseWriter, r *http.Request){ body, err := ioutil.ReadAll(r.Body) // err = ioutil.WriteFile("/tmp/request", body, 0644) if err != nil { panic(err.Error()) } var admissionReviewReq v1beta1.AdmissionReview if _, _, err := universalDeserializer.Decode(body, nil, &admissionReviewReq); err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Errorf("could not deserialize request: %v", err) } else if admissionReviewReq.Request == nil { w.WriteHeader(http.StatusBadRequest) errors.New("malformed admission review: request is nil") } fmt.Printf("Type: %v \t Event: %v \t Name: %v \n", admissionReviewReq.Request.Kind, admissionReviewReq.Request.Operation, admissionReviewReq.Request.Name, ) var pod apiv1.Pod err = json.Unmarshal(admissionReviewReq.Request.Object.Raw, &pod) if err != nil { fmt.Errorf("could not unmarshal pod on admission request: %v", err) } var patches []patchOperation labels := pod.ObjectMeta.Labels labels["example-webhook"] = "it-worked" patches = append(patches, patchOperation{ Op: "add", Path: "/metadata/labels", Value: labels, }) patchBytes, err := json.Marshal(patches) if err != nil { fmt.Errorf("could not marshal JSON patch: %v", err) } admissionReviewResponse := v1beta1.AdmissionReview{ Response: &v1beta1.AdmissionResponse{ UID: admissionReviewReq.Request.UID, Allowed: true, }, } admissionReviewResponse.Response.Patch = patchBytes bytes, err := json.Marshal(&admissionReviewResponse) if err != nil { fmt.Errorf("marshaling response: %v", err) } w.Write(bytes) }