Skip to content

Where validating admission is actually called from

Claim — createHandler does not call the validating admission chain. It builds a validation function and hands it to storage.Create, which invokes it. Mutating admission is called directly, before the handler ever reaches storage — so the two halves of admission are not two calls at the same level, and a trace that draws them that way is drawing a sequence rather than a call graph.

Rests on — module 3.1's entry-point question, and the running apiserver, which you are about to rebuild with instrumentation.

Topology — none. forge, the apiserver from the hand-start.

Do

  1. Before touching anything, predict in writing the order in which these five things happen for one kubectl create -f pod.yaml, and which function calls which:
  2. the request body is decoded into an object
  3. the mutating admission chain runs
  4. strategy.Validate runs
  5. the validating admission chain runs
  6. a key appears in etcd

  7. Read staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go — the createHandler function — and answer two questions from it:

  8. Which named local variable holds the thing that will run validating admission, and what type is it?
  9. Which argument position of the Create call does it occupy?

Then read staging/src/k8s.io/apiserver/pkg/registry/rest/create.go — BeforeCreate — and staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go — Create — and find the line that calls that argument.

  1. Now instrument. Add one klog.Infof at each of the five points, each tagged so they sort:
klog.Infof("ACADEMY 1-decoded %T", obj)
klog.Infof("ACADEMY 2-mutating-admission-returned")
klog.Infof("ACADEMY 3-strategy-validate")
klog.Infof("ACADEMY 4-createValidation-invoked")
klog.Infof("ACADEMY 5-storage-write")

Put them in the file that owns each event, not all in one file — the point of the exercise is which file each event belongs to. Rebuild and restart:

cd ~/src/kubernetes && GOGC=50 go build -gcflags=all="-N -l" -o ~/kube-apiserver ./cmd/kube-apiserver
pkill -f 'kube-apiserver --etcd-servers' ; sleep 2
# restart with the same flag block as before
  1. Drive one create through it:
kubectl --context=admin run trace --image=busybox --restart=Never -- sleep 3600

Observe

grep ACADEMY /tmp/apiserver.log
git -C ~/src/kubernetes diff --stat          # the instrumentation, as a patch

Expect — five lines in numeric order, which is the boring half. The interesting half is the stack, not the sequence: swap one klog.Infof for a klog.InfoS with debug.Stack() at point 4 and read who called it.

klog.Infof("ACADEMY 4-createValidation-invoked\n%s", debug.Stack())

Expect the caller of point 4 to be inside the generic store's Create, below the handler in the stack — not the handler itself. That is the claim. If your prediction had validating admission as a sibling call of storage.Create, this is where it is corrected, and it is a correction worth making now: the capstone trace is graded on citations surviving a hostile check, and "validating admission is called by the handler" is a citation that will not survive.

The likely stumble is the build: five edits across three files, and one of them is in a staging/ package, so a mistyped import fails in a directory you did not edit. go build ./staging/src/k8s.io/apiserver/... narrows it fast.

Write down — the five-point ordered list, each with the file:function it happens in, plus one sentence stating which pairs are sequential calls and which is a nested callback. This is the skeleton the capstone fills in with line numbers, and it is the phase's most reused note.

Teardown — kubectl --context=admin delete pod trace. Keep the instrumented tree — git stash it if you prefer a clean checkout for reading, but the reinvocation exercise and the CRD handler exercise both add to it. The apiserver and etcd stay up. No topology is up.