kubernetes/kubernetes
Patterns and conventions
Cross-cutting style and design conventions used everywhere in the repo. New code is expected to follow them.
Go style
- gofmt-clean, goimports-ordered. Enforced by
verify-gofmt.shandverify-imports.sh. - Standard library first, then external imports, then
k8s.io/...imports, then the local module. Thegoimportsconfiguration inhack/enforces this. - Receiver names are short (
c *Controller, notcontroller *Controller). - Exported types use full English (
PodStatus, notPdSt). - No global state except for registries (feature gate, scheme, codec, plugin registry).
- Error wrapping uses
fmt.Errorf("...: %w", err). Useerrors.Is/errors.Asto unwrap, never type assertions.
API conventions
The Kubernetes API has its own conventions document. The most important rules:
- Every object has
metadata,spec, andstatus.specis desired state;statusis observed state. Controllers reconcile spec → status. - Objects are versioned per API group (
apiVersion: apps/v1). Version bumps require a KEP and conversion functions. - Every field has a
// +k8s:tag controlling codegen. Read existing types instaging/src/k8s.io/api/core/v1/types.gofor the pattern. - Booleans are forbidden when a string enum could be used. The reason: enums grow gracefully; booleans don't.
omitemptyon optional fields. Pointers (*string,*int32) when "unset" needs to be distinguishable from "zero".
Controller pattern
Every controller in pkg/controller/ follows this skeleton:
type FooController struct {
client clientset.Interface
fooLister fooListers.FooLister
fooSynced cache.InformerSynced
queue workqueue.RateLimitingInterface
}
func (c *FooController) Run(ctx context.Context, workers int) {
defer c.queue.ShutDown()
if !cache.WaitForCacheSync(ctx.Done(), c.fooSynced) {
return
}
for i := 0; i < workers; i++ {
go wait.UntilWithContext(ctx, c.worker, time.Second)
}
<-ctx.Done()
}
func (c *FooController) worker(ctx context.Context) {
for c.processNextItem(ctx) {}
}
func (c *FooController) processNextItem(ctx context.Context) bool {
key, quit := c.queue.Get()
if quit {
return false
}
defer c.queue.Done(key)
err := c.syncHandler(ctx, key.(string))
if err == nil {
c.queue.Forget(key)
return true
}
c.queue.AddRateLimited(key)
return true
}controller_utils.go and controller_ref_manager.go in pkg/controller/ contain helpers for Pod creation, ownership management, and expectation tracking that every controller eventually needs.
Error handling
- Wrap, don't replace. Always preserve the underlying error so callers can
errors.Is(err, ...). - Type a domain error when the caller will switch on it.
apierrors.IsNotFound(err)fromstaging/src/k8s.io/apimachinery/pkg/api/errorsis the canonical example. - Never
panicin steady-state code. Bugs that should panic useruntime.HandleErrororklog.Fatalfat startup. - Return early. Stack-arrowed code is rejected in review.
Concurrency
- Use
context.Contextfor cancellation and timeouts. Nevertime.AfterFuncfor cancelling work. - Channels for coordination, mutexes for protecting state. Never both for the same field.
sync.RWMutexoversync.Mutexonly when the read/write ratio is heavily skewed.- Goroutines must be cancelable. Bare
go func(){...}()without a way to stop is a review red flag. wait.UntilWithContext,wait.PollUntilContextTimeout, andwait.NonSlidingUntilfromk8s.io/apimachinery/pkg/util/waitare the project's preferred polling primitives.
Logging
klog.V(N).InfoS("message", "key", value, ...). Use structured logging (InfoS/ErrorS), not the legacy formatted variant (Infof/Errorf).klog.FromContext(ctx)to inherit context-scoped logger fields. Many controllers attachpod,namespace, andnamekeys to the context-bound logger.- Avoid logging at level 0 from steady-state loops; that's reserved for genuine errors.
Metrics
- Use
k8s.io/component-base/metrics(themetrics.Gauge,metrics.Counter,metrics.Histogramconstructors). Don't import Prometheus directly except in code that wires up the registry. - New metrics need a
STABLE/BETA/ALPHAstability marker. - Metrics names must follow the existing pattern (
apiserver_request_duration_seconds,kubelet_pod_start_duration_seconds). Lower snake_case, base-unit suffix.
Feature gates
- Add a constant in
pkg/features/kube_features.go(or the equivalent file instaging/src/k8s.io/apiserver/pkg/features). - Wrap the new code path in
utilfeature.DefaultFeatureGate.Enabled(features.MyFeature). - Default
falsefor alpha; flip totrueand document graduation at beta. - Remove the gate (and the unconditional code path) at GA.
Cross-cutting concerns
- Don't import from
k8s.io/kubernetes/pkg/...into staging modules. The dependency arrow only goes the other way. The lint scriptstaging/publishing/...enforces this. - Don't import
pkg/api/v1directly. Usek8s.io/api/core/v1. Thepkg/api/tree is the internal hub-and-spoke versioning surface, not a public type set. - Don't add new top-level directories. New code finds a home under
pkg/,cmd/,staging/src/k8s.io/, ortest/. - Don't break existing compatibility. Field renames, enum reorderings, and serialization changes are forbidden on stable types.
Imports
_ "time/tzdata"is imported bycmd/kube-apiserver/apiserver.goandcmd/kube-controller-manager/controller-manager.goto embed the time-zone database for CronJobs._ "k8s.io/component-base/logs/json/register"registers the JSON log format on every binary._ "k8s.io/component-base/metrics/prometheus/clientgo"registers client-go Prometheus collectors.
These blank imports are part of the boot ritual and must be present in every new component binary.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.