feat(homeassistant): Gateway-API as an alternative to the Ingress

Group 3 of the #114 sweep, and the one chart where the convention had to bend
rather than the chart. homeassistant is the DOCUMENTED INGRESS EXCEPTION
(README): upstream's flexible values, a list of hosts each with its own list
of paths, instead of the repo's required domain/clusterIssuer pair. The
exception stays - nothing here unifies it.

NO httpRoute.hostnames KEY, which is the freesailarr shape from group 2 and
for the same reason: there is no ingress.domain to fall back to, and a
chart-global hostname list could not say which host entry - and therefore
which set of paths - it belonged to. So httpRoute carries only enabled and
parentRefs, the hostnames come from ingress.hosts, and the chart renders ONE
HTTPRoute PER ENTRY with one rule per path of that entry. One object with
several hostnames would be wrong, not merely different: an HTTPRoute's rules
apply to every hostname of the object, and per-host paths are this chart's
whole point. The schema rejects httpRoute.hostnames outright, so the absence
is enforced rather than merely intended.

ingress.hosts is therefore read in BOTH modes and is not an Ingress-only
value. ingress.className, ingress.tls and ingress.annotations are not read in
Gateway mode: class and certificate belong to the Gateway's listener, and
there is no annotation channel.

pathType IS THE SHARP EDGE, AND THE CHART'S DEFAULT IS THE AWKWARD VALUE.
Ingress has Exact/Prefix/ImplementationSpecific; the Gateway API has
Exact/PathPrefix/RegularExpression. Exact and Prefix map cleanly.
ImplementationSpecific - the default in values.yaml - means "ask the
controller" and has NO counterpart, so:

  - on "/" it renders as PathPrefix. Every controller treats a root
    ImplementationSpecific path as a prefix match, so the chart's own default
    works in this mode unchanged (verified: the untouched default renders a
    valid route).
  - on ANY OTHER path the render ABORTS, naming the value and asking for
    Prefix or Exact. In ingress-nginx - this chart's default class - an
    ImplementationSpecific sub-path is a REGEX, and PathPrefix is not one.
    Mapping it silently would change how the path matches while the render
    stayed green, which is exactly the class of defect #118 was.

RegularExpression is deliberately not offered as an escape hatch: its support
level is Implementation-specific, which decision 1 of #114 forbids.

NOTHING WAS MEASURED ON THE LIVE CLUSTER FOR THIS CHART, because there is
nothing to measure: homeassistant is NOT DEPLOYED in jk-cluster (checked
2026-10-12 - 23 Ingress objects cluster-wide, none this chart's, and no
workload of it in any namespace). So it is the one chart of the six with no
operator-supplied annotation to lose. values.yaml and NOTES.txt say that as
an absence of evidence about this installation rather than as a property of
the chart.

Route NAMES carry the entry's INDEX, not its hostname - a hostname is not a
dependable object-name fragment. Consequence named in values.yaml and in the
template: reordering ingress.hosts renames routes and therefore recreates
rather than edits them. Harmless, since a route holds no state and no
certificate.

ci/httproute/homeassistant.yaml has to SUPPLY HOSTS, which no other overlay in
the sweep does, and it adds coverage the install-test never had: this chart's
ingress.enabled defaults to FALSE and the fixture does not switch it on, so
no routing object was exercised at all before. Two entries on purpose - a
plain one, and one with two paths including an Exact - because that second
entry is what proves the chart renders one object per host rather than one
object with several hostnames. 5 assertions over 2 routes and 3 rules.

NOTES.txt asks $published where it asked ingress.enabled, and the
trusted_proxies section now warns that moving from an Ingress to a Gateway
CHANGES the proxy's pod network: a list carried over unchanged turns every
request into a 400 if the CIDR differs, or silently keeps attributing every
client to one address if it does not.

Minor: additive, defaults unchanged. Empty helm template diff against
origin/main, both with the fixture as it stands and with ingress.enabled=true.

Part of #114
This commit is contained in:
jklein authored and jklein committed 2026-10-13 08:59:09 +00:00
1 parent 3e969c5659
commit b10800da2a
6 files changed
+453 -22

No files matched your search

+79
View File
@@ -0,0 +1,79 @@
# Render-only overlay for ci/run-httproute-match-test.sh, applied LAST.
#
# Its one job is to put this chart into Gateway mode, because the convention
# ships with httpRoute.enabled: false and the test would otherwise be green
# over nothing. A chart that has an overlay here and still renders no
# HTTPRoute FAILS the test - that is the whole point of the file existing.
#
# An overlay and not a change to ci/homeassistant/test-values.yaml, and here
# that is more than the usual reason. This chart's ingress.enabled DEFAULT IS
# false and the install-test fixture does not switch it on, so the install
# exercises no routing object at all - as with garage. There is nothing to
# trade away: this overlay ADDS coverage that the install-test never had,
# rather than swapping one object for another.
#
# ingress.enabled: false is restated anyway. It is already the default, but the
# two modes are mutually exclusive and the chart aborts its own render when
# both are true, so writing it down here keeps this overlay correct if that
# default ever changes.
#
# AND THIS OVERLAY HAS TO SUPPLY HOSTS, which no other overlay in the sweep
# does. This chart is the DOCUMENTED INGRESS EXCEPTION: it has no
# ingress.domain, so there is no httpRoute.hostnames either - the hostnames and
# the paths come from ingress.hosts, and the chart renders ONE HTTPRoute PER
# ENTRY there. The chart's default entry is the placeholder
# "chart-example.local" with pathType ImplementationSpecific, which WOULD
# render (the chart maps ImplementationSpecific to PathPrefix on "/"), but it
# would exercise exactly one route with one rule and would leave the
# interesting half of this chart's flexible style untested.
#
# So the list below is deliberate, and each entry earns its place:
#
# 1. ha.ci.local with "/" and pathType Prefix - the ordinary case, and the
# clean Prefix -> PathPrefix mapping.
# 2. ha-alt.ci.local with TWO paths, one Prefix and one Exact - this is the
# part that cannot be expressed in a single HTTPRoute together with entry
# 1, because an HTTPRoute's rules apply to EVERY hostname of the object.
# It is therefore the test that the chart really renders one object per
# host entry rather than one object with several hostnames, and it
# exercises the Exact mapping and the multi-rule path at the same time.
#
# Both entries' rules are checked against the Service of the same render. The
# reference is by PORT NUMBER (service.port) rather than by the name "http" the
# Ingress uses, because backendRefs takes a PortNumber - so this overlay is
# what holds that number against the Service's.
#
# NOT covered here, and it cannot be:
#
# - that either hostname answers. No Gateway controller runs in CI and no
# route reaches "Accepted: True".
# - the pathType ImplementationSpecific ABORT on a non-root path. That is a
# render-failure path, and this test only runs renders it expects to
# succeed; it was exercised by hand instead (see the PR).
# - that Home Assistant accepts the Host header or reads X-Forwarded-For.
# Both live in the application's own configuration, which arrives from
# 1Password, and changing the data path changes the proxy address the
# trusted_proxies list has to name. NOTES.txt says so; nothing can assert
# it here.
#
# No URLRewrite filter is rendered by this chart in either mode, so the #118
# prefix/redirect assurance has no precondition to meet here.
ingress:
enabled: false
hosts:
- host: ha.ci.local
paths:
- path: /
pathType: Prefix
- host: ha-alt.ci.local
paths:
- path: /
pathType: Prefix
- path: /api/websocket
pathType: Exact
httpRoute:
enabled: true
parentRefs:
- name: public
namespace: gateway
sectionName: https