--- # Flux Kustomization: Intel GPU device plugin — reconciles ./platform/intel-gpu-plugin. # Makes the three prod nodes advertise `gpu.intel.com/i915`, which is the resource a later PR will # have `hardware.gpu: intel-quicksync` request in place of today's non-working /dev/dri hostPath. # # WHY ITS OWN KUSTOMIZATION, AND NOT ONE LINE IN platform/kustomization.yaml # # The cheap option was a line in the shared `platform` tier plus a new platform/ directory, and # neither path is sensitive in infra/gitea/approval-policy.json, so it would have needed no human # reviewer on the policy's terms. It is still the wrong place, for the reason the repo already # wrote down in platform/kustomization.yaml when Cilium was split out of that tier: # # * `platform` is `wait: true` over eleven-plus components, so its Ready condition cannot tell # you "the GPU plugin is wedged" from "longhorn is slow". # * The only emergency valve would be `flux suspend kustomization platform` — freezing storage, # TLS, DNS, ingress, monitoring, Keycloak and KubeVirt in order to stop touching a device # plugin. # * The tier's 10m interval / 5m timeout are explicitly called out there as WRONG for a 3-node # DaemonSet rollout. This is a 3-node DaemonSet rollout. # # And the consequence is worse here than for an average component, because of what sits downstream: # `platform` gates platform-config, which gates apps-wave-0 (wait:true), which gates apps-wave-1. # A plugin DaemonSet that never went healthy inside that tier would stall every app on "dependency # not ready" — i.e. a component added to fix a SILENT NO-OP (software transcoding) would have # acquired the ability to cause a REAL OUTAGE. That trade is the whole argument. # # The usual objection to this placement is the approval cost, and it does not apply: Gitea enforces # `required_approvals: 1` with `block_admin_merge_override: true` on EVERY PR here regardless of # path, and authors cannot self-approve. So this change needed the operator's approval either way; # touching clusters/** costs nothing extra and buys a blast radius of exactly one component. # # dependsOn: [cilium], WHICH IS THE MINIMUM AND ALSO THE EARLIEST # # Not platform-config (what headlamp/crafty/homepage/oauth2-proxy use) — those are leaf UIs that # genuinely need Keycloak + wildcard TLS + split-horizon DNS + Gateway. This plugin needs none of # it. It never uses the network and never talks to the API server; it talks to the local kubelet # over a unix socket. Its only real prerequisite is a working CNI, so its pod can get a sandbox at # all, and `cilium` already depends on `flux-system` (the root Kustomization that applies the # cluster-vars ConfigMap this one substitutes from), so that edge is transitively satisfied too. # # Depending on `cilium` rather than `platform` also gets the resource advertised EARLIEST — this # reconciles in parallel with the `platform` tier instead of behind all eleven of its components. # That matters on a cold start once the consumer PR lands: a Plex pod requesting a resource no node # advertises is Unschedulable, and apps-wave-0 is wait:true. Reconciling early shrinks that window # instead of widening it. It is not a GUARANTEE — see the ordering note below, which is a real # constraint on the next PR and not a formality. # # Kyverno (in `platform`) is deliberately NOT a dependency: every policy that could match this # workload is scoped to namespaces labelled app-namespace=true, and this namespace is labelled # "false" (platform/intel-gpu-plugin/namespace.yaml). So there is no admission race to lose. # # wait: true — A HEALTH SIGNAL, NOT A GATE. This is the one place this component departs from the # headlamp/crafty leaf template, and the departure is deliberate. Those run wait:false because a # leaf UI can be legitimately not-Ready for a long time during a cold start while it waits on # Keycloak/DNS/TLS, so its health is not a useful signal. This DaemonSet has no such dependency: it # reads sysfs and opens a socket, so on healthy nodes it should be Ready in seconds, and # not-Ready genuinely means BROKEN. wait:true turns `flux get kustomization intel-gpu-plugin` into # a one-line answer to "did the plugin come up on all three nodes", which is exactly the question # the operator must answer before the consumer PR may merge. # # NOTHING IN THE GRAPH dependsOn THIS, so wait:true gates nothing — the blast radius of an unhealthy # plugin is this Kustomization's own Ready condition and nothing else. Anyone later adding # `dependsOn: [intel-gpu-plugin]` is choosing to make plugin health gate their thing; for the app # waves that would hand a device plugin the power to stall every app, which is the thing this file # exists to prevent. Think before adding that edge. # # ORDERING — DO NOT REORDER THE STACK. The renderer change (catalog/render.py emitting # `gpu.intel.com/i915: 1` for `hardware.gpu: intel-quicksync` instead of the hostPath) must NOT # land until this plugin is merged AND confirmed advertising the resource on all three nodes. If # the renderer requests a resource no node advertises, the Plex pod is Unschedulable; apps-wave-0 is # wait:true and apps-wave-1 dependsOn it, so that stalls the whole app tier — turning today's silent # no-op into a real outage. This PR's only job is to make the resource exist. apiVersion: kustomize.toolkit.fluxcd.io/v1 kind: Kustomization metadata: name: intel-gpu-plugin namespace: flux-system spec: # Static vendored manifests with nothing to re-render; kustomize-controller is source-triggered, # so this is only the drift-correction floor. interval: 10m retryInterval: 2m # 10m, not the 5m the `platform` tier uses: a 3-node DaemonSet rollout under `wait: true` can # exceed 5m on a cold start (image pull on three nodes), and the Cilium split-out comment calls # out that exact mismatch as one of its reasons for leaving the shared tier. timeout: 10m prune: true # See the header: a health SIGNAL for a component nothing depends on, not a gate. wait: true dependsOn: - name: cilium # the CNI — the only real prerequisite; transitively gives us flux-system too sourceRef: kind: GitRepository name: flux-system path: ./platform/intel-gpu-plugin # Retained for parity with every other tier / future overlay knobs. This component references no # ${VARS} today, and its manifests contain no `$` literals for envsubst to eat (unlike # clusters/base/runbooks.yaml, which omits substitution for exactly that reason). postBuild: substituteFrom: - kind: ConfigMap name: cluster-vars # No SOPS decryption patch: this component carries no Secret, so clusters/dev adds nothing for it # (same as homepage/runbooks, unlike headlamp/crafty/oauth2-proxy/backup).