cozystack/packages/system/lineage-controller-webhook
Timofei Larkin 1dc02d44f4 refactor(lineage-controller-webhook): align with cozystack-api shape
Collapse the chart to a single Deployment shape that mirrors
cozystack-api: 2 replicas, soft (preferred) nodeAffinity to
node-role.kubernetes.io/control-plane via Exists, permissive
tolerations, soft podAntiAffinity on hostname, an unconditional
PodDisruptionBudget with maxUnavailable: 1, and a Service with
spec.trafficDistribution: PreferClose. The same shape works on
Talos / kubeadm / k3s and on managed Kubernetes / Cozy-in-Cozy
tenant clusters without per-distro overrides — fixing #2417 by
making the soft control-plane affinity gracefully fall back to
worker scheduling when no control-plane nodes are visible.

Drop the DaemonSet path entirely. The previous PR's deployment.enabled
toggle, the workload-kind switch in templates/workload.yaml, the
fail-guard for localK8sAPIEndpoint+nodeAffinity=[], the conditional
PDB, and the values-shape knobs for nodeAffinity and tolerations all
go away as a consequence. Override the standard Deployment fields
through the usual component-values mechanism if a non-default topology
is ever needed.

Mark localK8sAPIEndpoint.enabled deprecated and flip the default to
false. The flag injects KUBERNETES_SERVICE_HOST=status.hostIP, which
is only valid when the pod is actually scheduled on an apiserver-
bearing node. With the new soft control-plane affinity, the pod can
land off-control-plane and crash-loop. The latency motivation for the
flag is real but pending separate webhook performance work; once
addressed, the flag can be removed.

Revert all changes to packages/core/platform/images/migrations/migrations/20.
The earlier ds/...-or-deploy/... fallback was over-engineered: per
run-migrations.sh, migration 20 fires only when CURRENT < 20 (i.e.
direct upgrades from pre-0.37 to 1.3+), and that path is unsupported
anyway. The original ds/... rollout-status line is dead code on every
supported install and upgrade path.

Add helm-unittest coverage for: the default Deployment shape (replicas,
soft nodeAffinity, soft podAntiAffinity, tolerations); no env vars at
the default localK8sAPIEndpoint.enabled=false; PDB rendered
unconditionally with maxUnavailable: 1, including at replicas=1 (the
no-op case); replicas value drives spec.replicas; and that
localK8sAPIEndpoint.enabled=true does inject the env vars. The Service
test continues to assert trafficDistribution: PreferClose with no
internalTrafficPolicy.

Add a slim README documenting the topology, parameters, and the
deprecation note for localK8sAPIEndpoint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
2026-04-28 14:55:11 +03:00
..
images/lineage-controller-webhook Update go modules 2025-12-23 19:45:47 +01:00
templates refactor(lineage-controller-webhook): align with cozystack-api shape 2026-04-28 14:55:11 +03:00
tests refactor(lineage-controller-webhook): align with cozystack-api shape 2026-04-28 14:55:11 +03:00
.gitignore [lineage] Separate webhook from cozy controller 2025-10-14 10:50:28 +03:00
Chart.yaml [lineage] Separate webhook from cozy controller 2025-10-14 10:50:28 +03:00
Makefile refactor(lineage-controller-webhook): align with cozystack-api shape 2026-04-28 14:55:11 +03:00
README.md refactor(lineage-controller-webhook): align with cozystack-api shape 2026-04-28 14:55:11 +03:00
values.yaml refactor(lineage-controller-webhook): align with cozystack-api shape 2026-04-28 14:55:11 +03:00

lineage-controller-webhook

Cozystack system package for the lineage controller webhook — a mutating admission webhook that stamps "lineage" labels onto tenant workloads, linking each resource back to the Cozystack Application that ultimately owns it.

The webhook intercepts CREATE and UPDATE on pods, secrets, services, persistentvolumeclaims, ingresses.networking.k8s.io, and workloadmonitors.cozystack.io outside system namespaces. For each request it walks the ownership graph upward (Kubernetes ownerReferences, then the HelmRelease Flux installed the resource with, then the Cozystack Application-derived CRD that produced the HelmRelease) and writes the discovered application's group, kind and name as labels (apps.cozystack.io/application.{group,kind,name}) on the incoming object. Those labels let the aggregated API server, the Cozystack dashboard, and other lineage-aware consumers reason about which application a resource belongs to.

The webhook serves TLS on port 9443 with a cert-manager issued certificate, and runs with failurePolicy: Fail — every CREATE/UPDATE on the resources above is gated on the webhook being reachable.

Topology

The chart ships a single shape, modelled on cozystack-api:

  • Deployment with two replicas (override via replicas).
  • Soft nodeAffinity preferring node-role.kubernetes.io/control-plane (Exists, so it matches both Talos's empty value and k3s/kubeadm's "true"). Soft means: the pod lands on a control-plane node when one is reachable, and on any worker otherwise — no override needed for managed Kubernetes, Cozy-in-Cozy tenants, or any other cluster where control-plane nodes aren't visible.
  • Permissive tolerations ([{operator: Exists}]) so the pod can land on tainted control-plane nodes when the soft affinity is satisfiable.
  • Soft podAntiAffinity on kubernetes.io/hostname so replicas spread across nodes when possible (best-effort).
  • PodDisruptionBudget with maxUnavailable: 1, unconditional. At replicas: 1 it's a useful no-op (allows full drain); at replicas >= 2 it caps disruption to one pod.
  • Service with spec.trafficDistribution: PreferClose so the apiserver prefers a webhook endpoint on its own node when one exists, and transparently falls over to a remote endpoint otherwise. Requires Kubernetes ≥ 1.31; on older clusters the field is silently ignored and traffic uses default cluster-wide distribution.

Parameters

All keys live under the top-level lineageControllerWebhook: map.

Name Description Value
image Container image (digest-pinned) ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.3.0@sha256:e898…6fb0
debug Enable --zap-log-level=debug instead of info false
replicas Deployment replica count 2
localK8sAPIEndpoint.enabled Deprecated. See note below. false

localK8sAPIEndpoint.enabled (deprecated)

When enabled, this injects KUBERNETES_SERVICE_HOST=status.hostIP and KUBERNETES_SERVICE_PORT=6443 so the webhook talks to the kube-apiserver on its own node. It was originally added to avoid latency on the webhook-to-apiserver path, but it is only valid when the pod is actually scheduled on an apiserver-bearing node — which the chart's soft control-plane affinity no longer guarantees. With this flag enabled and the pod scheduled off a control-plane node, the controller will crash-loop dialing a non- apiserver hostIP. Slated for removal once the latency motivation is addressed in the webhook itself; leave disabled.