feat(ytdlp-web-player): Gateway-API as an alternative to the Ingress
Group 3 of the #114 sweep, last chart - and the one that carried the group-2 pattern this sweep was told to look for. It reads ingress.enabled OUTSIDE its Ingress template, in ytdlp-web-player.validate, and in Gateway mode ingress.enabled is false by construction, so that guard would have gone silent. Fixed with ytdlp-web-player.published = ingress.enabled OR httpRoute.enabled - the freesailarr.published / sure.publishedOverTls shape, identical to ingress.enabled while httpRoute.enabled is false, which is why it changes no existing render. AND THE GUARD IS NEEDED MORE IN GATEWAY MODE, NOT LESS. It refuses to publish the application and the oauth2-proxy on the same host: - In INGRESS mode the consequence of ignoring it is loud: ingress-nginx's admission webhook rejects the second Ingress and the Fleet bundle goes to ErrApplied. - In GATEWAY mode THE WEBHOOK NEVER FIRES. It compares Ingress objects with Ingress objects, and an HTTPRoute is a different kind - so nothing rejects anything, both objects stay valid, and one hostname is served over two data paths that are NOT equivalent: the proxy's Ingress goes through AUTHENTICATION and this chart's HTTPRoute goes straight to the application. Which one a client reaches depends on what DNS resolves to. That is an authentication bypass that comes and goes with a DNS record and that no Kubernetes object reports. So the Gateway-mode abort has its own message saying exactly that. Both messages exercised, plus the collision reached through httpRoute.hostnames rather than through the ingress.domain fallback, and the guard still firing when only the Deployment is rendered. THE SUBCHART HAS NO GATEWAY SUPPORT, and this chart cannot give it one - upstream's chart is consumed as it comes (README, "Chart dependencies"). It renders an Ingress or nothing. So in the RECOMMENDED configuration, where the proxy sits in the data path and owns the host, the publishing object is the SUBCHART'S INGRESS in both of this chart's modes, and turning httpRoute on buys nothing. The route is for the proxy-disabled case, or for the application on a host of its own - which is the documented unauthenticated bypass. Said in the template header, in values.yaml, in the schema and in the proxy branch of NOTES.txt, because someone migrating hosts will otherwise set the value and wonder why nothing moved. The 16k proxy-buffer-size this chart recommends is NOT lost by this switch, and that needed stating rather than assuming: it belongs on the PROXY's Ingress (oauth2-proxy.ingress.annotations) as the second half of the 4kB session-cookie trap, and the subchart keeps rendering an Ingress. This chart's own ingress.annotations default is {} and it sets nothing but the cert-manager annotation, so there is no chart-side annotation loss to name. CORRECTED CLAIM: values.yaml and the schema both said that everything the Ingress needs is read only inside its template, so switching it off freed BOTH ingress.domain and ingress.clusterIssuer. clusterIssuer is still freed; domain is now read by the HTTPRoute, so it is freed only with both modes off. The reason the claim was made still holds and is kept - the application has no base-URL setting and builds no absolute links. NOTHING WAS MEASURED ON A LIVE INGRESS OF THIS CHART, because there is none. Measured 2026-10-12: the release runs in namespace jlab-player with both Deployments Ready, and NOT ONE of the cluster's 23 Ingress objects 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 Helm values are readable. Recorded as the open question it is, in values.yaml and in the overlay, rather than guessed at. Minor: additive, defaults unchanged. Empty helm template diff against origin/main with ci/ytdlp-web-player/test-values.yaml. Part of #114
This commit is contained in:
1 parent
b10800da2a
commit
e4b41577ae
7 files changed
+504
-36
No files matched your search
@@ -1,4 +1,23 @@
|
||||
{{- /*
|
||||
NOTHING IN CI READS THIS FILE. `helm template` does not emit notes and no
|
||||
workflow or script under ci/ reads it, so every claim below carries
|
||||
review-only assurance - it is checked by being read, not by a stage. A
|
||||
rendering ERROR here does fail `helm template`, which is all the pipeline can
|
||||
see of it.
|
||||
*/ -}}
|
||||
{{- $replicas := include "ytdlp-web-player.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.ytdlpWebPlayer.replicas) -}}
|
||||
{{- $httpRoute := .Values.httpRoute | default dict -}}
|
||||
{{- /*
|
||||
"Published" rather than "has an Ingress": the two modes are alternatives, so
|
||||
every paragraph that asked ingress.enabled was really asking whether this
|
||||
chart publishes the application (Gitea #114). Same helper the host guard uses.
|
||||
*/ -}}
|
||||
{{- $published := eq (include "ytdlp-web-player.published" .) "true" -}}
|
||||
{{- $hostnames := $httpRoute.hostnames | default list -}}
|
||||
{{- if not $hostnames -}}
|
||||
{{- $hostnames = list .Values.ingress.domain -}}
|
||||
{{- end -}}
|
||||
{{- $ownHost := first $hostnames -}}
|
||||
{{- $res := fromYaml (include "ytdlp-web-player.effectiveResources" .Values.ytdlpWebPlayer.resources) -}}
|
||||
{{- $eph := index ($res.limits | default dict) "ephemeral-storage" | default "the computed ephemeral-storage limit" -}}
|
||||
{{- $startup := fromYaml (include "ytdlp-web-player.startupProbe" .Values.ytdlpWebPlayer.startupProbe) -}}
|
||||
@@ -22,23 +41,66 @@ ACCESS — THROUGH THE PROXY
|
||||
way into the application. Certificate and TLS secret are the subchart's too —
|
||||
this chart's ingress.clusterIssuer does not reach it; set the cert-manager
|
||||
annotation under oauth2-proxy.ingress.annotations.
|
||||
|
||||
AN INGRESS, AND IT STAYS ONE IN BOTH OF THIS CHART'S MODES. The subchart has
|
||||
no Gateway-API support and this chart cannot give it one (upstream's chart is
|
||||
consumed as it comes), so httpRoute.enabled on THIS chart does not move the
|
||||
proxy off its Ingress. In this configuration — the recommended one — turning
|
||||
it on buys nothing.
|
||||
{{- else if .Values.ingress.enabled }}
|
||||
|
||||
ACCESS
|
||||
ACCESS — INGRESS MODE (ingress.enabled: true, httpRoute.enabled: false)
|
||||
https://{{ .Values.ingress.domain }}
|
||||
|
||||
The certificate is requested by cert-manager through the "{{ .Values.ingress.clusterIssuer }}"
|
||||
cluster issuer. Until it has been issued the browser shows a warning; follow it with
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} get certificate tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress
|
||||
{{- else if $httpRoute.enabled }}
|
||||
|
||||
ACCESS — GATEWAY MODE (httpRoute.enabled: true, ingress.enabled: false)
|
||||
{{- range $hostnames }}
|
||||
https://{{ . }}
|
||||
{{- end }}
|
||||
|
||||
This release renders ONE HTTPRoute ({{ .Release.Name }}-{{ .Chart.Name }}-httproute) and nothing
|
||||
else of the routing path — no Gateway, no class, no Certificate, no policy
|
||||
object. Three things therefore belong to the cluster:
|
||||
|
||||
1. The Gateway-API CRDs. Without them this install would have failed, so
|
||||
they are present — nothing to check.
|
||||
2. The Gateway named in httpRoute.parentRefs
|
||||
{{- range $httpRoute.parentRefs }}
|
||||
{{ .name }}{{ if .namespace }} in namespace {{ .namespace }}{{ end }}{{ if .sectionName }}, listener {{ .sectionName }}{{ end }}
|
||||
{{- end }}
|
||||
must exist AND its listener must allow routes from this namespace
|
||||
({{ .Release.Namespace }}) in allowedRoutes. A rejected attachment is visible only
|
||||
in the route's status:
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} get httproute {{ .Release.Name }}-{{ .Chart.Name }}-httproute -o yaml
|
||||
|
||||
Read status.parents[].conditions — "Accepted: True" and
|
||||
"ResolvedRefs: True" is what a working attachment looks like. An empty
|
||||
status means no controller has looked at the route at all.
|
||||
3. The TLS certificate for the hostname, on the Gateway's listener.
|
||||
ingress.clusterIssuer is NOT used in this mode and is not required: the
|
||||
chart renders no Certificate here, so there is no
|
||||
tls-{{ .Release.Name }}-{{ .Chart.Name }}-ingress secret.
|
||||
|
||||
ingress.annotations is inert in this mode — no annotation channel in the
|
||||
Gateway API. Note which annotation that is NOT about: the 16k
|
||||
proxy-buffer-size this chart recommends belongs on the PROXY's Ingress, and
|
||||
the subchart renders an Ingress whatever this chart does, so it is
|
||||
unaffected.
|
||||
{{- else }}
|
||||
|
||||
ACCESS — NO INGRESS
|
||||
ingress.enabled is false, so this release has no Ingress and no certificate.
|
||||
ACCESS — NEITHER INGRESS NOR HTTPROUTE
|
||||
ingress.enabled and httpRoute.enabled are both false, so this release has no
|
||||
Ingress, no HTTPRoute and no certificate.
|
||||
Both ingress.domain and ingress.clusterIssuer are unnecessary here — unlike
|
||||
in some other charts in this repo, nothing outside the Ingress template reads
|
||||
the domain: this application builds no absolute URLs and has no base-URL
|
||||
setting. Reach it inside the cluster:
|
||||
in some other charts in this repo, nothing but the two routing templates
|
||||
reads the domain: this application builds no absolute URLs and has no
|
||||
base-URL setting. Reach it inside the cluster:
|
||||
|
||||
kubectl -n {{ .Release.Namespace }} port-forward svc/{{ .Release.Name }}-{{ .Chart.Name }}-service 8080:80
|
||||
|
||||
@@ -48,14 +110,16 @@ ACCESS — NO INGRESS
|
||||
SCALED TO ZERO
|
||||
{{ if .Values.scaleDown }}scaleDown: true{{ else }}ytdlpWebPlayer.replicas: 0{{ end }} — the Deployment runs with replicas: 0, so nothing answers
|
||||
until you set it back and upgrade again. Service{{ if .Values.ingress.enabled }} and Ingress stay in place,
|
||||
so the certificate is not re-issued.{{ else }} stays in place.{{ end }} Nothing is lost that was not
|
||||
so the certificate is not re-issued.{{ else if $httpRoute.enabled }} and HTTPRoute stay in place, so the
|
||||
route keeps pointing at a Service with no endpoints — the Gateway answers
|
||||
with an error of its own rather than with nothing.{{ else }} stays in place.{{ end }} Nothing is lost that was not
|
||||
going to be lost anyway: the video cache is an emptyDir (see below).
|
||||
{{- if $proxy.enabled }}
|
||||
Note that scaleDown does NOT reach the subchart — the proxy keeps running and
|
||||
will answer with a gateway error. Set oauth2-proxy.replicaCount: 0 as well.
|
||||
{{- end }}
|
||||
{{ end }}
|
||||
{{- if and .Values.ingress.enabled (not $proxy.enabled) }}
|
||||
{{- if and $published (not $proxy.enabled) }}
|
||||
THIS RELEASE IS OPEN TO EVERYONE WHO KNOWS THE URL
|
||||
The application has NO authentication of any kind, and this chart adds none
|
||||
while oauth2-proxy.enabled is false. What an anonymous visitor can do here is
|
||||
@@ -73,22 +137,41 @@ THIS RELEASE IS OPEN TO EVERYONE WHO KNOWS THE URL
|
||||
enabled: true
|
||||
ingress:
|
||||
enabled: true
|
||||
hosts: ["{{ .Values.ingress.domain }}"]
|
||||
hosts: ["{{ $ownHost }}"]
|
||||
ingress:
|
||||
enabled: false # the proxy owns the host from then on
|
||||
{{- if $httpRoute.enabled }}
|
||||
httpRoute:
|
||||
enabled: false # and so does this one: the SUBCHART HAS NO
|
||||
# GATEWAY SUPPORT, so the proxy publishes over
|
||||
# an Ingress either way. Leaving this on for the
|
||||
# same host is refused by the chart; leaving it
|
||||
# on for a different host is the bypass below.
|
||||
{{- end }}
|
||||
|
||||
Anything else in front of it — an ingress-nginx auth annotation, a network
|
||||
policy, a VPN-only ingress class — has to come from ingress.annotations or
|
||||
from outside this chart.
|
||||
from outside this chart{{ if $httpRoute.enabled }}, and NONE of it is available in Gateway mode:
|
||||
there is no annotation channel, so an auth annotation has no counterpart.
|
||||
Authentication would have to come from the Gateway itself or from the proxy{{ end }}.
|
||||
{{- end }}
|
||||
{{- if and $proxy.enabled .Values.ingress.enabled }}
|
||||
{{- if and $proxy.enabled $published }}
|
||||
|
||||
WARNING — THE APP'S OWN INGRESS IS A WAY PAST THE PROXY
|
||||
Both oauth2-proxy.enabled and ingress.enabled are true. The chart only
|
||||
WARNING — THE APP'S OWN {{ if $httpRoute.enabled }}ROUTE{{ else }}INGRESS{{ end }} IS A WAY PAST THE PROXY
|
||||
Both oauth2-proxy.enabled and {{ if $httpRoute.enabled }}httpRoute.enabled{{ else }}ingress.enabled{{ end }} are true. The chart only
|
||||
refuses this when both claim the SAME host; here they do not, so it renders —
|
||||
but https://{{ .Values.ingress.domain }} reaches the application directly, with no
|
||||
but https://{{ $ownHost }} reaches the application directly, with no
|
||||
authentication in front of it, whatever the proxy does on its own host.
|
||||
Unless that is deliberate, set ingress.enabled=false.
|
||||
Unless that is deliberate, set {{ if $httpRoute.enabled }}httpRoute.enabled=false{{ else }}ingress.enabled=false{{ end }}.
|
||||
{{- if $httpRoute.enabled }}
|
||||
|
||||
AND IN THIS MODE THE SAME-HOST CASE IS THE ONE WORTH FEARING, which is why
|
||||
the chart refuses it: ingress-nginx's admission webhook compares Ingress
|
||||
objects with Ingress objects, so it never sees an HTTPRoute. Two valid
|
||||
objects would then serve one hostname — the proxy's Ingress through
|
||||
authentication, this chart's route straight to the application — and which
|
||||
one a client reaches would depend on DNS alone, with nothing reporting it.
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $proxy.enabled }}
|
||||
|
||||
|
||||
Reference in new issue
Block a user