This blog is the 2nd part of a blog post on how to write custom logic for your kubernetes objects. The first post can be found here https://gauravsarma1992.medium.com/kubernetes-operators-using-kubebuilder-7db99559120c which covers the approach to building your own kubernetes controller using Golang.

In this post, we will cover the best approach to write your own kubernetes webhooks. Webhooks are usually interceptors which can be used in 2 ways here:

  • Mutating — Changing the payload before the custom object is created
  • Validating — Ensuring the payload is proper, if it’s not, the CRD can be prevent from being created

In this post, we will be covering only Validating Webhooks.

The logic for the webhook will be very simple. If the task is set to True by default, we will reject the request.

Use kubebuilder, let’s scaffold the webhook logic.

kubebuilder create webhook –group todo –version v1 –kind TodoList –programmatic-validation

Modify the required files by uncommenting the parts for enabling the validating webhooks.

config/crd/kustomization.yaml

resources:  
\- bases/todo.sarmag.co\_todolists.yaml  
#+kubebuilder:scaffold:crdkustomizeresource  
  
patchesStrategicMerge:  
\# \[WEBHOOK\] To enable webhook, uncomment all the sections with \[WEBHOOK\] prefix.  
\# patches here are for enabling the conversion webhook for each CRD  
\- patches/webhook\_in\_todolists.yaml  
#+kubebuilder:scaffold:crdkustomizewebhookpatch  
  
\# \[CERTMANAGER\] To enable cert-manager, uncomment all the sections with \[CERTMANAGER\] prefix.  
\# patches here are for enabling the CA injection for each CRD  
\- patches/cainjection\_in\_todolists.yaml  
#+kubebuilder:scaffold:crdkustomizecainjectionpatch  
  
\# the following config is for teaching kustomize how to do kustomization for CRDs.  
configurations:  
\- kustomizeconfig.yaml

**_config/default/kustomization.yaml_**

namespace: custom-k8-controller-system  
  
namePrefix: custom-k8-controller-  
  
bases:  
\- ../crd  
\- ../rbac  
\- ../manager  
\# \[WEBHOOK\] To enable webhook, uncomment all the sections with \[WEBHOOK\] prefix including the one in  
\# crd/kustomization.yaml  
\- ../webhook  
\# \[CERTMANAGER\] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.  
\- ../certmanager  
\# \[PROMETHEUS\] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.  
#- ../prometheus  
  
patchesStrategicMerge:  
\# Protect the /metrics endpoint by putting it behind auth.  
\# If you want your controller-manager to expose the /metrics  
\# endpoint w/o any authn/z, please comment the following line.  
\- manager\_auth\_proxy\_patch.yaml  
  
\# Mount the controller config file for loading manager configurations  
\# through a ComponentConfig type  
#- manager\_config\_patch.yaml  
  
\# \[WEBHOOK\] To enable webhook, uncomment all the sections with \[WEBHOOK\] prefix including the one in  
\# crd/kustomization.yaml  
\- manager\_webhook\_patch.yaml  
  
\# \[CERTMANAGER\] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.  
\# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.  
\# 'CERTMANAGER' needs to be enabled to use ca injection  
\- webhookcainjection\_patch.yaml  
  
\# the following config is for teaching kustomize how to do var substitution  
vars:  
\# \[CERTMANAGER\] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.  
\- name: CERTIFICATE\_NAMESPACE \# namespace of the certificate CR  
  objref:  
    kind: Certificate  
    group: cert-manager.io  
    version: v1  
    name: serving-cert \# this name should match the one in certificate.yaml  
  fieldref:  
    fieldpath: metadata.namespace  
\- name: CERTIFICATE\_NAME  
  objref:  
    kind: Certificate  
    group: cert-manager.io  
    version: v1  
    name: serving-cert \# this name should match the one in certificate.yaml  
\- name: SERVICE\_NAMESPACE \# namespace of the service  
  objref:  
    kind: Service  
    version: v1  
    name: webhook-service  
  fieldref:  
    fieldpath: metadata.namespace  
\- name: SERVICE\_NAME  
  objref:  
    kind: Service  
    version: v1  
    name: webhook-service

Modify the ValidateCreate method in todolist_webhook.go code

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type  
func (r \*TodoList) ValidateCreate() error {  
 todolistlog.Info("validate create", "name", r.Name)  
 if r.Spec.Task == "" {  
  err := errors.New("task cannot be empty")  
  return err  
 }  
 return nil  
}  

Once this is done, build the docker image, load it on the k8s cluster and then deploy it.

make docker-build IMG=gsarma/k8s-operators:v1  
kind load docker-image gsarma/k8s-operators:v1 --name k8s-operators  
make deploy IMG=gsarma/k8s-operators:v1

All that’s left is to create a todolist object with an empty task and it should fail the request.

apiVersion: todo.sarmag.co/v1
kind: TodoList
metadata:
name: jack
namespace: operator-namespace
spec:
task: ""

Enjoy reading and implementing your own custom kubernetes webhook!!

I hope you liked the article. Please let me know if you have any queries regarding the article. Happy reading!!

References#