<p>Admission Controllers (Plugins) and Admission Webhooks look very similar on the surface. What is the actual, functional difference?</p>

I'll Eat My Hat
<p>Admission Controllers are conflated to mean both Admission Plugins and Admission Webhooks by experts and even the <a href="https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/" rel="nofollow noreferrer">official documentation</a>.
I will call them Admission Plugins and Admission Webhooks to prevent confusion.</p>
<p>Admission Plugins are <strong>not an API resource</strong>. They can't be created by cluster administrators. They are <a href="https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/pkg/kubeapiserver/options/plugins.go#L105-L139" rel="nofollow noreferrer">hardcoded into the Kubernetes source code.</a></p>
<p>Specifically, Admission Plugins are a tool with limitless power. Since they are baked into <code>kube-apiserver</code>, they have the ability to <a href="https://github.com/kubernetes/kubernetes/blob/ea0764452222146c47ec826977f49d7001b0ea8c/plugin/pkg/admission/storage/storageclass/setdefault/admission.go#L69-L73" rel="nofollow noreferrer">simply request for resources without authentication.</a> They use this power to implement the <code>DefaultStorageClass</code> Admission Plugin, which checks all of the <code>StorageClass</code>es to find one with a default annotation applied to it.</p>
<p>Admission Webhooks on the other hand, are actually <a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#validatingadmissionwebhook" rel="nofollow noreferrer">an implementation of an Admission Plugin</a>, with the extra power removed. Note how a <code>Webhook.Validator</code> <a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/webhook/admission#Validator" rel="nofollow noreferrer">does not have the ability to access any resources</a> besides the exact object being validated at the time.
Admission Webhooks <a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#validatingwebhookconfiguration-v1beta1-admissionregistration-k8s-io" rel="nofollow noreferrer"><strong>are API resources</strong></a>, and are usually the first thing recommended when implementing a validator for a Kubernetes Operator, or a CRD in general. Despite this,  <a href="https://github.com/operator-framework/operator-sdk/issues/4179#issuecomment-761105450" rel="nofollow noreferrer">signed certificates are required</a>, which can make you abandon the whole idea.</p>
<h2>What does this mean for the average developer?</h2>
<p>To write a validator that relies on checking existing objects in the cluster, you need to go through extra steps by using the <a href="https://github.com/kubernetes/client-go" rel="nofollow noreferrer">Kubernetes Golang Client API</a>, which relies on having a service account token with the correct RBAC applied.</p>
<p>You can also <a href="https://pkg.go.dev/k8s.io/apimachinery/pkg/apis/meta/v1#Status" rel="nofollow noreferrer">update your object with a Status instead</a>, explaining the reason validation failed. If you are writing a Kubernetes Operator, that can be written in the reconciliation loop.</p>
<p>If you have written a fancy UI for your custom resource, you should be able to read these statuses and expose them to the user in a nicer way. Unfortunately, OpenShift 4.X users are not able to add fancy UIs to the OpenShift Web Console, and will have to settle for exposing <code>Route</code>s.</p>
