{{/* HTTPRoute - the Gateway-API alternative to ytdlp-web-player.ingress.yaml (Gitea #114). READ THE SUBCHART PARAGRAPH FIRST, because it decides whether this mode is useful at all for a given installation. THE oauth2-proxy SUBCHART HAS NO GATEWAY SUPPORT, AND THIS CHART CANNOT GIVE IT ONE. Upstream's chart is consumed as it comes - that is the standing rule for dependencies (README, "Chart dependencies"): where its defaults fall short of this repository's conventions, the shortfall is DOCUMENTED and never patched. It renders an Ingress or nothing. So in the RECOMMENDED configuration - oauth2-proxy.enabled true, the proxy in the data path, the proxy owning the host and this chart's own routing object switched off - the object that publishes the release is the SUBCHART'S INGRESS, in both of this chart's modes. Setting httpRoute.enabled there buys nothing and changes nothing about how the host is served. This route is for the other two cases: - the proxy switched off entirely (oauth2-proxy.enabled false), the chart publishing the application itself; - the application on a host of ITS OWN, next to the proxy's host - which is allowed and means exactly what it says: an unauthenticated way past the proxy. NOTES.txt says so in both modes. AND THAT SECOND CASE IS WHY THE HOST GUARD CHANGED. ytdlp-web-player.validate used to ask ingress.enabled, which is false by construction in this mode, so the guard went silent exactly where it matters most. It now asks ytdlp-web-player.published (ingress.enabled OR httpRoute.enabled). The difference is not cosmetic: in Ingress mode a host claimed twice is rejected LOUDLY by ingress-nginx's admission webhook, while in Gateway mode the webhook never fires - it compares Ingress objects with Ingress objects, and an HTTPRoute is a different kind. Two valid objects then serve one hostname over two data paths, one through authentication and one straight to the application, and which one a client reaches depends on DNS. See the helper's comment for the full argument. WHAT THIS RENDERS, AND WHAT IT MUST NOT Only an HTTPRoute with parentRefs. No Gateway, no gatewayClassName, no Certificate, no implementation-specific policy object - the subset that looks identical on nginx-gateway-fabric, Cilium, Envoy Gateway and Istio (decision 1 of #114). Class and certificate belong to the PLATFORM, on the Gateway and its listeners, in cluster-config. WHAT THIS MODE DROPS, AND WHAT IT DOES NOT ingress.annotations is inert here - no annotation channel in the Gateway API. For this chart the measured picture is unusual, so it is worth being exact about which annotation lives where: - The 16k proxy-buffer-size this chart's values recommend is NOT on this chart's Ingress. It belongs on the PROXY's Ingress (oauth2-proxy.ingress.annotations), because it is the second half of the 4kB session-cookie trap: the response carrying the proxy's session cookie does not fit ingress-nginx's default 4k buffer and the browser gets a 502 AFTER a successful login. The subchart still renders an Ingress in this mode, so that annotation is UNAFFECTED by this switch. It would only be lost if the proxy itself ever moved behind a Gateway, which this chart cannot do. - This chart's own ingress.annotations default is {} and the chart sets nothing but the cert-manager annotation, so there is no chart-side loss to name. NOTHING WAS MEASURED ON A LIVE INGRESS OF THIS CHART, because there is none. Checked on 2026-10-12: the release runs in namespace jlab-player (Services jlab-player-ytdlp-web-player-service and jlab-player-oauth2-proxy, both Deployments Ready) and NOT ONE of the 23 Ingress objects in the cluster is in that namespace - neither this chart's nor the subchart's. What publishes it instead could NOT be determined: this workspace's identity may not list httproutes or secrets, so neither a hand-written HTTPRoute nor the release's own Helm values are readable. Stated as the open question it is, rather than guessed at. */}} {{- /* Null-safe like every other block a template here dereferences (#99): an override written without children - "httpRoute:" with its keys deleted - is YAML null, and .Values.httpRoute.enabled would abort with a nil pointer. */ -}} {{- $httpRoute := .Values.httpRoute | default dict -}} {{- if and $httpRoute.enabled .Values.ingress.enabled -}} {{- fail "ingress.enabled and httpRoute.enabled are both true - set exactly one of them. The Ingress and the HTTPRoute would publish the same Service under the same hostname over two different data paths, which is already a conflict at DNS level: one name, two addresses, and which one a client reaches is luck. ingress.enabled defaults to TRUE in this chart, so turning the Gateway mode on means setting ingress.enabled: false in the same values file. A migration runs over a SECOND hostname (set httpRoute.hostnames explicitly while the Ingress keeps ingress.domain), never over parallel operation of both - and if the oauth2-proxy subchart is in the data path, mind that it has no Gateway support and publishes over an Ingress regardless." -}} {{- end -}} {{- if $httpRoute.enabled -}} {{- include "ytdlp-web-player.validate" . -}} {{- $parentRefs := $httpRoute.parentRefs | default list -}} {{- if not $parentRefs -}} {{- fail "httpRoute.enabled is true but httpRoute.parentRefs is empty - an HTTPRoute without a parent attaches to no Gateway. It is created, it is schema-valid, and it routes NOTHING, with no event and no condition that says so; the only symptom is a hostname that does not answer. Name the cluster's Gateway, e.g. httpRoute.parentRefs: [{name: public, namespace: gateway}]." -}} {{- end -}} {{- /* One source of truth for the hostname: an empty httpRoute.hostnames means [ingress.domain]. Two values that have to agree drift apart. AND THIS READ CHANGES THE CHART'S OWN ACCOUNT OF ITSELF. values.yaml and the schema both stated that everything the Ingress needs is read only inside the Ingress template, which is why switching it off freed BOTH ingress.domain and ingress.clusterIssuer. The second half still holds - the application has no base-URL setting and builds no absolute links, so it needs no hostname of its own - but ingress.domain is now read HERE as well. Both have been corrected rather than left standing. clusterIssuer is still freed by this mode: no Certificate is rendered. */ -}} {{- $hostnames := $httpRoute.hostnames | default list -}} {{- if not $hostnames -}} {{- $hostnames = list (required "A Domain/Host must be provided: set ingress.domain (used as the HTTPRoute's single hostname when httpRoute.hostnames is empty) or set httpRoute.hostnames explicitly" .Values.ingress.domain) -}} {{- end -}} apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: {{ .Release.Name }}-{{ .Chart.Name }}-httproute spec: parentRefs: {{- toYaml $parentRefs | nindent 4 }} hostnames: {{- toYaml $hostnames | nindent 4 }} rules: {{- /* The Ingress counterpart is `path: / , pathType: Prefix` on the application Service. The port is the Service's port NUMBER and not its name "http": backendRefs takes a PortNumber, so unlike the Ingress this reference cannot be made by name. 80 is what the Service publishes (and what ytdlp-web-player.appServiceAddress hands the proxy as its upstream), so the three agree; ci/run-httproute-match-test.sh holds the pair for the case where they stop agreeing. */}} - matches: - path: type: PathPrefix value: / backendRefs: - name: {{ .Release.Name }}-{{ .Chart.Name }}-service port: 80 {{- end }}