Files
jklein f530f58b1d
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Build rss-bridge (pull_request) Successful in 8s
Validate Helm Charts / Trivy config rss-bridge (pull_request) Successful in 23s
Install-test / Install-test rss-bridge (pull_request) Successful in 1m6s
Validate Helm Charts / Install-test (pull_request) Successful in 1m6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
Publish RSS-Bridge Helm Chart / Publish RSS-Bridge Helm Chart (push) Successful in 12s
SonarQube Analysis / SonarQube Analysis (push) Successful in 45s
docs(rss-bridge): #51 was closed, and the cause was never identified (2.1.1)
PR #124 merged at 12:25 and the maintainer closed #51 one second later, on
the strength of that round's green install-test. Three statements that
merged with #124 are stale as a result, and they are stale in the
direction that matters - they describe a failing test and an open issue:

  - rss-bridge/templates/rss-bridge.httproute.yaml and values.yaml said
    the install-test "fails on the Gitea runner (#51)", in the present
    tense, and values.yaml added "and that test is still red".
  - ci/httproute/rss-bridge.yaml said "#51 is not closed on the strength
    of a colour", which was true when written and is now the opposite of
    what happened.

Replaced with what is actually known, and the emphasis moved to the part
that outlives the colour: the test went green on a render byte-identical
to main, which rules the chart out and leaves the runner - and THE CAUSE
WAS NEVER IDENTIFIED. The diagnostic programme in #51 (docker info for
userns and the dind configuration, the same image in a local k3d without
remapping) was not executed. So a recurrence is a fresh finding rather
than a known quirk, and the overlay comment now says so where whoever
sees the red will be reading.

Also carries the wording change that was already on this branch when
#124 merged: the reproduced green is phrased as "every run of that branch
since" rather than pinned to a count, because each commit recording the
count invalidated it - a third green arrived with the push after "two".

Patch bump, per the rule #40 was argued on: a comment in a chart
directory is still a change to that chart, so it is a patch plus a
publish. No template output changes - the render for the fixture values
is byte-identical to the pre-#124 main, which the tests re-confirm.
2026-10-13 10:28:09 +00:00

145 lines
8.8 KiB
YAML

{{/*
The Gateway-API alternative to rss-bridge.ingress.yaml (Gitea #114), and the
LAST chart of that sweep: 22 charts carried an Ingress template and 21 an
HTTPRoute template, and the difference was this one. It was held back because
its install-test had been failing on the Gitea runner (#51), not for anything
about its shape - and that test went green in the very PR that converted it,
on a render byte-identical to main, which is what ruled the chart out and left
the runner. #51 was closed on the strength of that round; the cause was never
identified, so a recurrence is a fresh finding rather than a known quirk.
WHAT THIS RENDERS, AND WHAT IT DELIBERATELY DOES NOT
Only an HTTPRoute with parentRefs. No Gateway, no gatewayClassName, no
Certificate, and no implementation-specific policy object (nginx-gateway-
fabric's ClientSettingsPolicy, Envoy Gateway's ClientTrafficPolicy or whatever
the next one calls it). That subset is the part which looks IDENTICAL on
nginx-gateway-fabric, Cilium, Envoy Gateway and Istio - keeping to it is what
lets the cluster's implementation be chosen, or changed, without touching this
chart again. A policy object here would bind the chart to one implementation
and to a CRD whose presence it cannot check.
So the class and the certificate move from this chart to the PLATFORM: they sit
on the Gateway and its listeners, which belong in cluster-config, not here.
ONE OBJECT FOR ALL THREE HOSTNAMES, BECAUSE NOTHING DIFFERS PER HOST
spec.hostnames is a LIST and its rules apply to every hostname of the object.
That is a problem for a chart whose paths differ per host, and this chart is
the opposite case: every hostname serves the SAME Service at "/" - measured on
the live Ingress, which carries three host rules with byte-identical path and
backend. So one object with three hostnames, not three objects.
Contrast, because both shapes exist in this repository and the difference is
not style: homeassistant renders one route PER host entry (its paths differ per
host), matrix-synapse renders two (homeserver and delegation want different
paths and different backends), and the keycloak realms of Gitea #122 would need
one each (app-root differs per host). Here a second object would carry the same
rule twice.
WHY httpRoute.hostnames IS ITS OWN VALUE AND DOES NOT REUSE ingress.hosts
The other charts of the sweep fall into two camps, and this chart is in
neither. Where a chart has ingress.domain - a single public name - empty
hostnames falls back to it, so there is one source of truth (cyberchef).
Where a chart has no ingress.domain, httpRoute has no hostnames key at all and
the names come from the values that already hold them, because a chart-global
list could not say which app or which host entry it belonged to (freesailarr,
homeassistant).
This chart has no ingress.domain either, but its hostnames are unambiguous:
one Service, one path, so a chart-global list means exactly one thing. What
makes reuse of ingress.hosts wrong here is the other half of those entries -
each carries an OPTIONAL tlsSecretName, which exists only to keep three grown
certificates alive on their existing secret names (see the deviation note in
values.yaml). In Gateway mode TLS sits on the Gateway's listener, so those
names address nothing. Reusing the list would carry a structure whose second
field is meaningless into the new mode and invite somebody to set it there.
Hence: httpRoute.hostnames is a plain list of strings and is MANDATORY in this
mode. There is no ingress.domain to fall back to, and an empty list would
render a route matching every request that reaches its listener - on a shared
Gateway that means answering for hostnames belonging to other releases.
AND THEREFORE rss-bridge.tlsGroups IS INERT IN THIS MODE
That helper exists for one purpose: group hosts by TLS secret name so that
hosts sharing a name share one certificate with several SANs. With the
certificate on the listener there is nothing left for it to group. It is read
by the Ingress template and by NOTES.txt's Ingress branch, both behind
ingress.enabled, so this mode simply never calls it. That is a SIMPLIFICATION
and is named as one rather than quietly translated: the most intricate part of
this chart's Ingress has no counterpart here because it has no job here.
NOTHING IS LOST WITH THE ANNOTATIONS, AND THAT IS MEASURED, NOT ASSUMED
Group 3 of the sweep screened the LIVE Ingress objects, not just the charts,
because an operator's annotation can fill a gap the chart never sets and this
mode drops ingress.annotations - four of its six charts carried exactly such an
annotation. The same screen was run here against the running object
(2026-10-13, read-only kubectl): it carries the cert-manager cluster-issuer
annotation plus Helm and Rancher/Fleet bookkeeping, and NOT ONE
nginx.ingress.kubernetes.io annotation. So this is the first chart of the sweep
whose Gateway branch in NOTES.txt has no loss to explain. ingress.annotations
is still inert in this mode - whatever a future installation puts there is
gone - but there is nothing there today to lose.
WHY A VALUE AND NOT AUTO-DETECTION
.Capabilities.APIVersions is unreliable at template time (it answers from the
cluster helm happens to be pointed at, and not at all for `helm template`),
while an HTTPRoute without the Gateway-API CRDs fails at INSTALL time. A chart
that guessed would therefore sometimes render an object the cluster rejects and
sometimes silently render nothing. So the mode is a value.
*/}}
{{- /*
Null-safe like every other block this chart dereferences (#99): an override
written without children - "httpRoute:" with the keys below it deleted - is
YAML null, and `.Values.httpRoute.enabled` would abort the render with a nil
pointer. An empty block means "chart defaults apply", and all three defaults
here are empty or false, so the whole block collapses to "no HTTPRoute".
*/ -}}
{{- $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 hostnames 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. Note that ingress.enabled DEFAULTS TO TRUE in this chart, so switching to Gateway mode means setting ingress.enabled: false explicitly. A migration runs over a SECOND hostname (give httpRoute.hostnames a name the Ingress does not serve) and never over parallel operation of both." -}}
{{- end -}}
{{- if $httpRoute.enabled -}}
{{- $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 -}}
{{- $hostnames := $httpRoute.hostnames | default list -}}
{{- if not $hostnames -}}
{{- fail "httpRoute.enabled is true but httpRoute.hostnames is empty - there is no hostname to publish. Unlike most charts here this one has NO ingress.domain to fall back to, and ingress.hosts is deliberately not reused: its entries carry tlsSecretName values that address nothing once TLS sits on the Gateway's listener. An HTTPRoute with no hostnames also does not render nothing - it matches EVERY request reaching its listener, so on a shared Gateway this release would answer for hostnames belonging to other releases. Set the public names explicitly, e.g. httpRoute.hostnames: [rss-bridge.example.com, feeds.example.com]." -}}
{{- 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 one backend
Service, repeated once per host rule - RSS-Bridge is served at the root and
has no second path, so one rule covers all hostnames of this object.
The port is the Service's port NUMBER and not its name: backendRefs takes a
PortNumber, so unlike the Ingress - which references the targetPort by the
name rss-bridge-port - this reference cannot be made by name, and the 80
below has to track rss-bridge.service.yaml by hand.
ci/run-httproute-match-test.sh is what holds that pair: it fails when a
backendRef names a Service, or a port, the same render does not create.
*/}}
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: {{ .Release.Name }}-{{ .Chart.Name }}-service
port: 80
{{- end }}