Kubernetes runtime
k8s.io/apimachinery/pkg/runtime — the package
controller-runtime
and
client-go
use to encode and decode Kubernetes objects.
import (
"k8s.io/apimachinery/pkg/runtime"
)
What does “Scheme” mean?
A Scheme is a phone book of types. Kubernetes talks in
YAML/JSON using apiVersion + kind. Go talks in
structs. The Scheme maps one to the other:
YAML/JSON Go struct
--------- ---------
apiVersion: v1 <--> *corev1.Pod
kind: Pod
apiVersion: apps/v1 <--> *appsv1.Deployment
kind: Deployment
apiVersion: demo.io/v1 <--> *demov1.Widget (your CRD)
kind: Widget
Without a Scheme, the client sees kind: Widget and does not
know which Go type to put the bytes into. Typical error:
no kind "Widget" is registered for version "demo.io/v1".
What runtime.NewScheme() does
It creates an empty phone book. Nothing is registered yet — not
even Pod. You then fill it with AddToScheme(scheme) for
every API group you will read or write.
scheme := runtime.NewScheme() // empty book
// Register built-in types (Pod, Service, Deployment, ...)
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
// Register your CRD types
utilruntime.Must(demov1.AddToScheme(scheme))
After that you pass scheme into the Manager or client so
every Get / List / Update can encode and decode those types.
Very simple example
Goal: decode this YAML into a real Go Pod struct. That
works only after Pod is registered on the Scheme.
package main
import (
"fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
)
func main() {
// 1. Empty phone book
scheme := runtime.NewScheme()
// 2. Teach it: kind Pod + apiVersion v1 → *corev1.Pod
if err := clientgoscheme.AddToScheme(scheme); err != nil {
panic(err)
}
yamlBytes := []byte(`
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: default
`)
// 3. Decoder uses the Scheme to pick the Go type
decoder := serializer.NewCodecFactory(scheme).UniversalDeserializer()
obj, gvk, err := decoder.Decode(yamlBytes, nil, nil)
if err != nil {
panic(err)
}
pod := obj.(*corev1.Pod)
fmt.Println("decoded kind:", gvk.Kind) // Pod
fmt.Println("pod name:", pod.Name) // nginx
}
Comment out AddToScheme and the same YAML fails — the empty
Scheme does not know what a Pod is.
Same Scheme in a controller
Operators do the same thing once at startup, then hand the Scheme to controller-runtime:
var scheme = runtime.NewScheme()
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(demov1.AddToScheme(scheme)) // your CRD
}
func main() {
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme, // Manager + cache + client all share this book
})
// ...
}