feat: Gateway-API as an alternative to the Ingress in five annotation charts (group 2) #117

Merged
jklein merged 2 commits from feat/gateway-api-group2 into main 2026-10-12 21:16:22 +02:00
Member

Group 2 of the #114 sweep: the charts whose behaviour hangs off an nginx annotation. calibre-web-automated, outline, paperless-ngx and zipline carry proxy-body-size; freesailarr carries rewrite-target. Out of scope and untouched: rss-bridge (frozen, #51) and group 3.

Each chart gets the form the pilot fixed and group 1 repeated: httpRoute off by default, a template rendering only an HTTPRoute with parentRefs, render aborts on both modes at once (naming both value paths) and on an empty parentRefs, empty hostnames meaning [ingress.domain], a schema entry closed against unknown keys with an open parentRef definition, and a NOTES.txt that names the active mode and what it costs.

Minor, additive, defaults unchanged — helm template with each chart's CI values is byte-identical to origin/main for all five.

The upload limit is documented, not rebuilt

Decision 2 of the issue, and the Gateway branch of each NOTES.txt says it in that chart's own terms rather than in one copied sentence:

Chart value what the Gateway branch says
calibre-web-automated cwa.maxUploadFileSize this is an e-book library; a book above the Gateway's default is rejected before CWA sees a byte, and nothing says a chart value was ignored
outline ingress.maxUploadFileSize the ceiling is the smaller of outline.maxUploadSize and whatever the Gateway enforces, and the chart can only name the first
paperless-ngx paperless.files.maxUploadFileSize caps the web-UI half only; the consume directory passes no proxy and has no limit in any mode
zipline config.maxUploadFileSize this is an upload service, and the limit was the one that actually bit — far below the app's pinned UPLOADER_ADMIN_LIMIT=500mb

freesailarr sets no body-size annotation and needs none, but ingress.annotations is the only lever an operator has for one (qBittorrent takes torrent-file uploads), so its Gateway branch says an annotation set there is gone. That is the corrected sieve from group 1 — needs one, not sets one — applied to this group.

Interaction with #12, named and deliberately not resolved

outline ships outline.maxUploadSize: '262144000' (~250 MB) against ingress.maxUploadFileSize: 25m — a factor of ten, smaller wins, which is #12. In Gateway mode the ingress half disappears, so the effective ceiling becomes the Gateway's: neither of the two numbers the chart documents is necessarily the one that decides. That moves #12 rather than fixing it, and fixing it here would mean rendering a policy object — exactly the portability this sweep buys. Stated in the template header, in values.yaml, in the schema and in the Gateway branch of NOTES.txt. #12 is untouched by this PR.

Four findings that were not in the plan

1. freesailarr read ingress.enabled outside its Ingress template — the sure class, and this one was live. freesailarr.sharedIngressApps was gated on ingress.enabled, and that list is read by the <APP>__SERVER__URLBASE environment variables as well as by the routing objects. In Gateway mode ingress.enabled is false by construction, so the list would have come out empty while the route served radarr, sonarr and prowlarr under /radarr, /sonarr, /prowlarr: UrlBase unset, the apps answering at the sub-path with assets and API calls linked at the host root, a broken UI behind a Ready pod. The helper's two guards (seerr/mediathekarr only at /, no duplicate paths on the shared host) would have gone unenforced in that mode for the same reason.

Measured rather than argued:

origin/main, ingress.enabled=false  -> 0 URLBASE variables rendered
this branch, Gateway mode (overlay) -> PROWLARR__SERVER__URLBASE=/prowlarr
                                       RADARR__SERVER__URLBASE=/radarr
                                       SONARR__SERVER__URLBASE=/sonarr

Fixed with one helper, freesailarr.published = ingress.enabled OR httpRoute.enabled — the same shape as sure.publishedOverTls, and identical to ingress.enabled while httpRoute.enabled is false, which is why this stays Minor.

The other four charts were checked on the same pattern and on the related one (something derived from ingress.domain or ingress.className). Findings per chart are in the "what was checked" table below.

2. paperless-ngx was the last chart in the repository rendering its Ingress unconditionally — it needed an ingress.enabled switch, defaulting to true. Not uniformity: without a second value path there is nothing for the mutual-exclusion abort to name, and the Ingress together with its unconditional required() on clusterIssuer would have kept rendering in Gateway mode — a value demanded for something the chart does not render, which is issue #114's Folge 1 and README guideline 12. The default keeps the render byte-identical. This is the one scope addition in the PR; say so if you would rather have had it separately.

3. paperless-ngx's PAPERLESS_URL had no required() of its own. It relied on the Ingress template's, which stops firing the moment the Ingress is switchable. With httpRoute.hostnames set and ingress.domain empty it would have rendered https:// and refused every login POST as a CSRF failure. It now carries its own, naming what the value is for — unchanged output, since the value was already mandatory in practice.

4. calibre-web-automated claimed, in values.yaml and in its schema, that ingress.domain is read nowhere but in the Ingress template. The HTTPRoute reads it, so the claim holds only while both modes are off. Corrected rather than left standing — the same correction garage needed in group 1.

freesailarr: the Gateway API is less machinery here, not a translation

rewrite-target applies per Ingress object. qBittorrent has no base path, so its prefix has to be stripped while its neighbours must not be — which forced a second Ingress object on the shared host, and that object then had to

  • repeat the first object's TLS secret name, so neither depends on an assumption about whose TLS configuration nginx applies to which paths,
  • omit the cert-manager annotation so only one Certificate is requested — conditionally, for the case where no other object renders at all, and
  • match an ImplementationSpecific regex path (<path>(/|$)(.*)) whose capture group the rewrite refers to.

A URLRewrite filter applies per rule. All four disappear: one route, one rule per app, the filter on qBittorrent's rule, a plain PathPrefix:

- matches:
    - path: { type: PathPrefix, value: "/qbittorrent" }
  filters:
    - type: URLRewrite
      urlRewrite:
        path: { type: ReplacePrefixMatch, replacePrefixMatch: / }
  backendRefs:
    - name: ci-freesailarr-qbittorrent-service
      port: 8080

It is also the only chart without httpRoute.hostnames: it has no ingress.domain either (the documented exception), and a chart-global hostname list could not say which of the ten apps it belonged to — so the hostnames stay in ingress.host and <app>.ingress.host, which is where this chart's single source of truth already is. Both ingress shapes work unchanged: one HTTPRoute per app with its own host, plus exactly one shared-host route with one rule per app.

What was checked, per chart

Chart reads ingress.enabled outside the Ingress template? ingress.domain outside it? result
calibre-web-automated no — NOTES.txt only no, and that claim was written down and is now false claim corrected in values + schema; domain needed again in Gateway mode via the route's own required()
outline no — NOTES.txt only yes, URL, with its own required() domain mandatory in every mode, already correct; outline.forceHttps is its own value and is not derived from ingress.enabled — checked, no sure-class defect
paperless-ngx the value did not exist yes, PAPERLESS_URL, without a required() switch added; required() added — findings 2 and 3
zipline no — NOTES.txt only yes, CORE_DEFAULT_DOMAIN, with its own required() domain mandatory in every mode, already correct
freesailarr yes, in _helpers.tpl → sharedIngressApps → UrlBase env vars no ingress.domain at all finding 1

Verification

Run locally, all five charts:

  • ci/helm-dependencies.sh paperless-ngx first — the valkey subchart, or every helm command exits 1.
  • helm lint --strict at defaults and with CI values plus the Gateway overlay — clean (freesailarr's [INFO] icon is recommended is pre-existing on main).
  • helm template against both modes; helm package for all five.
  • An empty helm template diff against origin/main with unchanged values, five times. One was not empty at first: a # comment added next to PAPERLESS_URL is part of the rendered manifest. Moved into a non-rendered {{/* */}} block, with the reason in the comment — the same cause and the same fix as in group 1.
  • ci/run-httproute-match-test.sh over all charts: 36 assertions held over 15 charts, 0 failed (group 1 left it at 20 over 10). freesailarr contributes 8 — its own-host route plus five rules of the shared route. ci/run-quantity-render-test.sh (28 exercised, 0 failed) and ci/run-selector-match-test.sh (5 selectors, 0 failed) both clean.
  • Schema in both directions: the overlay is accepted; httpRoute.bogus is rejected (additional properties 'bogus' not allowed), a parentRef without name is rejected (missing property 'name'), and httpRoute.hostnames is rejected on freesailarr, which deliberately has no such key.
  • Fail paths exercised, not assumed: both modes at once on all five (paperless and freesailarr naming the true default explicitly), empty parentRefs, Gateway mode without a hostname (calibre aborts in the route, zipline in its StatefulSet first — which is the point), and httpRoute.hostnames set with ingress.domain empty on paperless, which the new required() catches.
  • The NOTES.txt branch where neither mode is on renders for all five — it is not covered by the template diff, because helm template does not emit notes.

What is not proven, by construction

That traffic flows. No Gateway controller runs in CI and none will, so no route ever reaches Accepted: True. What the pipeline can show: the object is schema-valid (install-test, real CRDs — paid by cyberchef) and it names a Service and a port the same render creates (the render test).

Nothing was run in a cluster for this PR: no k3d, no Docker, no Trivy, no kubectl apply.

The Gateway branch of NOTES.txt is executed but never read by any stage. Verified both halves: a deliberate error in NOTES.txt makes helm template exit 1, so the render test with the overlay does catch a broken Gateway branch — but helm template does not print notes, and the install-test installs in Ingress mode, so the text is asserted by nothing and displayed nowhere in CI. Reviewing it is reviewing it by reading.

Also in this PR

README's HTTPRoute-test section carried a stale figure: it still documented the pilot's 4 assertions over 2 charts although group 1 had taken it to 20 over 10. Corrected together with the new number.

Wiki: the five chart pages plus Chart-Style-Guide and CI-CD.

What remains of #114 after this: group 3 (homeassistant, keycloak, bulwark, matrix-synapse, stalwart, ytdlp-web-player) and rss-bridge, which is frozen — #114 cannot be closed while #51 is open.

Part of #114

Group 2 of the #114 sweep: the charts whose behaviour hangs off an nginx annotation. `calibre-web-automated`, `outline`, `paperless-ngx` and `zipline` carry `proxy-body-size`; `freesailarr` carries `rewrite-target`. Out of scope and untouched: `rss-bridge` (frozen, #51) and group 3. Each chart gets the form the pilot fixed and group 1 repeated: `httpRoute` off by default, a template rendering **only** an `HTTPRoute` with `parentRefs`, render aborts on both modes at once (naming both value paths) and on an empty `parentRefs`, empty `hostnames` meaning `[ingress.domain]`, a schema entry closed against unknown keys with an open `parentRef` definition, and a `NOTES.txt` that names the active mode and what it costs. **Minor, additive, defaults unchanged** — `helm template` with each chart's CI values is byte-identical to `origin/main` for all five. ## The upload limit is documented, not rebuilt Decision 2 of the issue, and the Gateway branch of each `NOTES.txt` says it in that chart's own terms rather than in one copied sentence: | Chart | value | what the Gateway branch says | | --- | --- | --- | | `calibre-web-automated` | `cwa.maxUploadFileSize` | this is an e-book library; a book above the Gateway's default is rejected before CWA sees a byte, and nothing says a chart value was ignored | | `outline` | `ingress.maxUploadFileSize` | the ceiling is the smaller of `outline.maxUploadSize` and **whatever the Gateway enforces**, and the chart can only name the first | | `paperless-ngx` | `paperless.files.maxUploadFileSize` | caps the **web-UI** half only; the consume directory passes no proxy and has no limit in any mode | | `zipline` | `config.maxUploadFileSize` | this is an upload service, and the limit was the one that actually bit — far below the app's pinned `UPLOADER_ADMIN_LIMIT=500mb` | `freesailarr` sets no body-size annotation and needs none, but `ingress.annotations` is the only lever an operator has for one (qBittorrent takes torrent-file uploads), so its Gateway branch says an annotation set there is gone. That is the corrected sieve from group 1 — *needs* one, not *sets* one — applied to this group. ## Interaction with #12, named and deliberately not resolved `outline` ships `outline.maxUploadSize: '262144000'` (~250 MB) against `ingress.maxUploadFileSize: 25m` — a factor of ten, smaller wins, which is #12. **In Gateway mode the ingress half disappears**, so the effective ceiling becomes the Gateway's: neither of the two numbers the chart documents is necessarily the one that decides. That *moves* #12 rather than fixing it, and fixing it here would mean rendering a policy object — exactly the portability this sweep buys. Stated in the template header, in `values.yaml`, in the schema and in the Gateway branch of `NOTES.txt`. **#12 is untouched by this PR.** ## Four findings that were not in the plan **1. `freesailarr` read `ingress.enabled` outside its Ingress template — the `sure` class, and this one was live.** `freesailarr.sharedIngressApps` was gated on `ingress.enabled`, and that list is read by the `<APP>__SERVER__URLBASE` environment variables as well as by the routing objects. In Gateway mode `ingress.enabled` is `false` by construction, so the list would have come out **empty** while the route served radarr, sonarr and prowlarr under `/radarr`, `/sonarr`, `/prowlarr`: UrlBase unset, the apps answering at the sub-path with assets and API calls linked at the host root, a broken UI behind a `Ready` pod. The helper's two guards (seerr/mediathekarr only at `/`, no duplicate paths on the shared host) would have gone unenforced in that mode for the same reason. Measured rather than argued: ``` origin/main, ingress.enabled=false -> 0 URLBASE variables rendered this branch, Gateway mode (overlay) -> PROWLARR__SERVER__URLBASE=/prowlarr RADARR__SERVER__URLBASE=/radarr SONARR__SERVER__URLBASE=/sonarr ``` Fixed with one helper, `freesailarr.published` = `ingress.enabled OR httpRoute.enabled` — the same shape as `sure.publishedOverTls`, and identical to `ingress.enabled` while `httpRoute.enabled` is `false`, which is why this stays Minor. The other four charts were checked on the same pattern and on the related one (something derived from `ingress.domain` or `ingress.className`). Findings per chart are in the "what was checked" table below. **2. `paperless-ngx` was the last chart in the repository rendering its Ingress unconditionally — it needed an `ingress.enabled` switch, defaulting to `true`.** Not uniformity: without a second value path there is nothing for the mutual-exclusion abort to name, and the Ingress together with its **unconditional** `required()` on `clusterIssuer` would have kept rendering in Gateway mode — a value demanded for something the chart does not render, which is issue #114's *Folge 1* and README guideline 12. The default keeps the render byte-identical. **This is the one scope addition in the PR; say so if you would rather have had it separately.** **3. `paperless-ngx`'s `PAPERLESS_URL` had no `required()` of its own.** It relied on the Ingress template's, which stops firing the moment the Ingress is switchable. With `httpRoute.hostnames` set and `ingress.domain` empty it would have rendered `https://` and refused every login POST as a CSRF failure. It now carries its own, naming what the value is for — unchanged output, since the value was already mandatory in practice. **4. `calibre-web-automated` claimed, in `values.yaml` and in its schema, that `ingress.domain` is read nowhere but in the Ingress template.** The HTTPRoute reads it, so the claim holds only while both modes are off. Corrected rather than left standing — the same correction `garage` needed in group 1. ## `freesailarr`: the Gateway API is less machinery here, not a translation `rewrite-target` applies per Ingress **object**. qBittorrent has no base path, so its prefix has to be stripped while its neighbours must not be — which forced a **second Ingress object** on the shared host, and that object then had to - repeat the first object's TLS secret name, so neither depends on an assumption about whose TLS configuration nginx applies to which paths, - **omit** the cert-manager annotation so only one `Certificate` is requested — conditionally, for the case where no other object renders at all, and - match an `ImplementationSpecific` **regex** path (`<path>(/|$)(.*)`) whose capture group the rewrite refers to. A `URLRewrite` filter applies per **rule**. All four disappear: one route, one rule per app, the filter on qBittorrent's rule, a plain `PathPrefix`: ```yaml - matches: - path: { type: PathPrefix, value: "/qbittorrent" } filters: - type: URLRewrite urlRewrite: path: { type: ReplacePrefixMatch, replacePrefixMatch: / } backendRefs: - name: ci-freesailarr-qbittorrent-service port: 8080 ``` It is also the only chart **without** `httpRoute.hostnames`: it has no `ingress.domain` either (the documented exception), and a chart-global hostname list could not say which of the ten apps it belonged to — so the hostnames stay in `ingress.host` and `<app>.ingress.host`, which is where this chart's single source of truth already is. Both ingress shapes work unchanged: one `HTTPRoute` per app with its own host, plus exactly one shared-host route with one rule per app. ## What was checked, per chart | Chart | reads `ingress.enabled` outside the Ingress template? | `ingress.domain` outside it? | result | | --- | --- | --- | --- | | `calibre-web-automated` | no — `NOTES.txt` only | **no**, and that claim was written down and is now false | claim corrected in values + schema; `domain` needed again in Gateway mode via the route's own `required()` | | `outline` | no — `NOTES.txt` only | yes, `URL`, with its own `required()` | `domain` mandatory in every mode, already correct; `outline.forceHttps` is its own value and is **not** derived from `ingress.enabled` — checked, no `sure`-class defect | | `paperless-ngx` | the value did not exist | yes, `PAPERLESS_URL`, **without** a `required()` | switch added; `required()` added — findings 2 and 3 | | `zipline` | no — `NOTES.txt` only | yes, `CORE_DEFAULT_DOMAIN`, with its own `required()` | `domain` mandatory in every mode, already correct | | `freesailarr` | **yes**, in `_helpers.tpl` → `sharedIngressApps` → UrlBase env vars | no `ingress.domain` at all | finding 1 | ## Verification Run locally, all five charts: - `ci/helm-dependencies.sh paperless-ngx` first — the valkey subchart, or every helm command exits 1. - `helm lint --strict` at defaults **and** with CI values plus the Gateway overlay — clean (`freesailarr`'s `[INFO] icon is recommended` is pre-existing on `main`). - `helm template` against both modes; `helm package` for all five. - **An empty `helm template` diff against `origin/main` with unchanged values, five times.** One was not empty at first: a `#` comment added next to `PAPERLESS_URL` is part of the rendered manifest. Moved into a non-rendered `{{/* */}}` block, with the reason in the comment — the same cause and the same fix as in group 1. - `ci/run-httproute-match-test.sh` over all charts: **36 assertions held over 15 charts, 0 failed** (group 1 left it at 20 over 10). `freesailarr` contributes 8 — its own-host route plus five rules of the shared route. `ci/run-quantity-render-test.sh` (28 exercised, 0 failed) and `ci/run-selector-match-test.sh` (5 selectors, 0 failed) both clean. - Schema in both directions: the overlay is accepted; `httpRoute.bogus` is rejected (`additional properties 'bogus' not allowed`), a `parentRef` without `name` is rejected (`missing property 'name'`), and `httpRoute.hostnames` is rejected on `freesailarr`, which deliberately has no such key. - Fail paths exercised, not assumed: both modes at once on all five (paperless and freesailarr naming the `true` default explicitly), empty `parentRefs`, Gateway mode without a hostname (`calibre` aborts in the route, `zipline` in its StatefulSet first — which is the point), and `httpRoute.hostnames` set with `ingress.domain` empty on paperless, which the new `required()` catches. - The `NOTES.txt` branch where **neither** mode is on renders for all five — it is not covered by the template diff, because `helm template` does not emit notes. ## What is not proven, by construction **That traffic flows.** No Gateway controller runs in CI and none will, so no route ever reaches `Accepted: True`. What the pipeline can show: the object is schema-valid (install-test, real CRDs — paid by `cyberchef`) and it names a Service and a port the same render creates (the render test). **Nothing was run in a cluster for this PR**: no k3d, no Docker, no Trivy, no `kubectl apply`. **The Gateway branch of `NOTES.txt` is executed but never read by any stage.** Verified both halves: a deliberate error in `NOTES.txt` makes `helm template` exit 1, so the render test with the overlay does catch a broken Gateway branch — but `helm template` does not print notes, and the install-test installs in Ingress mode, so the *text* is asserted by nothing and displayed nowhere in CI. Reviewing it is reviewing it by reading. ## Also in this PR README's HTTPRoute-test section carried a **stale figure**: it still documented the pilot's 4 assertions over 2 charts although group 1 had taken it to 20 over 10. Corrected together with the new number. Wiki: the five chart pages plus `Chart-Style-Guide` and `CI-CD`. What remains of #114 after this: group 3 (`homeassistant`, `keycloak`, `bulwark`, `matrix-synapse`, `stalwart`, `ytdlp-web-player`) and `rss-bridge`, which is frozen — **#114 cannot be closed while #51 is open.** Part of #114
jlab-bot added 1 commit 2026-10-12 20:17:34 +02:00
feat: Gateway-API as an alternative to the Ingress in five annotation charts
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 14s
Validate Helm Charts / Selector match (pull_request) Successful in 14s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 7s
Validate Helm Charts / Build outline (pull_request) Successful in 7s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 8s
Validate Helm Charts / Build zipline (pull_request) Successful in 8s
Validate Helm Charts / Build calibre-web-automated (pull_request) Successful in 7s
Validate Helm Charts / Trivy config calibre-web-automated (pull_request) Successful in 19s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 24s
Validate Helm Charts / Trivy config outline (pull_request) Successful in 17s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 19s
Validate Helm Charts / Trivy config zipline (pull_request) Successful in 19s
Install-test / Install-test calibre-web-automated (pull_request) Successful in 2m11s
Install-test / Install-test freesailarr (pull_request) Successful in 3m55s
Install-test / Install-test outline (pull_request) Successful in 2m44s
Install-test / Install-test paperless-ngx (pull_request) Successful in 3m52s
Install-test / Install-test zipline (pull_request) Successful in 2m6s
Validate Helm Charts / Install-test (pull_request) Successful in 14m50s
bd1fdc09a0
Group 2 of the #114 sweep: the charts whose behaviour hangs off an nginx
annotation - calibre-web-automated, outline, paperless-ngx, zipline
(proxy-body-size) and freesailarr (rewrite-target).

Each gets the form the pilot (cyberchef, PR #115) fixed and group 1 (PR #116)
repeated: an httpRoute block off by default, a template rendering ONLY an
HTTPRoute with parentRefs - no Gateway, no gatewayClassName, no Certificate, no
implementation-specific policy object - render aborts on both modes at once
(naming both value paths), on an empty parentRefs and on a missing hostname,
empty hostnames meaning [ingress.domain], a schema entry closed against unknown
keys with an open parentRef definition, and a NOTES.txt that names the active
mode and what the mode costs.

Minor, additive, defaults unchanged: `helm template` with each chart's CI values
is byte-identical to origin/main for all five.

The upload limit is DOCUMENTED, not rebuilt (decision 2 of the issue). The
Gateway API knows no body size; a policy object would bind the chart to one
implementation. So the Gateway branch of NOTES.txt says, per chart and in the
chart's own terms, that the value is NOT ENFORCED and names where to set it: an
e-book library (calibre), an upload service (zipline), the web-UI half of the
document intake while the consume directory keeps no limit at all (paperless),
and the open #12 pair (outline).

Four findings that were not in the plan, each argued in the file it changes:

  freesailarr gated freesailarr.sharedIngressApps on ingress.enabled, and that
  list is read by the <APP>__SERVER__URLBASE variables as well as by the routing
  objects. In Gateway mode it would have come out EMPTY while the route served
  radarr, sonarr and prowlarr under their prefixes: UrlBase unset, assets and API
  calls linked at the host root, broken UI behind a Ready pod. The helper's two
  guards (seerr/mediathekarr only at "/", no duplicate paths) would have gone
  unenforced there too. Fixed with freesailarr.published (ingress.enabled OR
  httpRoute.enabled) - the same shape as sure.publishedOverTls in group 1, and
  identical to ingress.enabled while httpRoute.enabled is false. Measured: on
  origin/main with ingress.enabled: false the render emits ZERO URLBASE
  variables; with this change in Gateway mode it emits three.

  paperless-ngx was the last chart in the repository rendering its Ingress
  UNCONDITIONALLY. Without an ingress.enabled switch there is no second value
  path for the mutual-exclusion abort to name, and the Ingress plus its
  unconditional required() on clusterIssuer would have kept rendering in Gateway
  mode - a value demanded for nothing, which is issue #114's "Folge 1" and
  README guideline 12. The switch was added, defaulting to true, so the render is
  unchanged.

  paperless-ngx PAPERLESS_URL had no required() of its own: it relied on the
  Ingress template's, which stops firing the moment the Ingress is switchable.
  With httpRoute.hostnames set and ingress.domain empty it would have rendered
  "https://" and refused every login POST as a CSRF failure. It now carries its
  own required(), naming what the value is for.

  calibre-web-automated claimed, in values.yaml and in its schema, that
  ingress.domain is read nowhere but in the Ingress template. The HTTPRoute reads
  it, so the claim now holds only while both modes are off. Corrected rather than
  left standing - the same correction garage needed in group 1.

freesailarr is where the Gateway API is LESS machinery than what it replaces,
not a translation of it. rewrite-target applies per Ingress OBJECT, so
qBittorrent (no base path, prefix must be stripped) needed a second Ingress on
the shared host, which then had to repeat the first object's TLS secret name,
omit the cert-manager annotation conditionally so only one Certificate is
requested, and match an ImplementationSpecific regex path for the rewrite's
capture group. A URLRewrite filter applies per RULE: one route, one rule per
app, the filter on qBittorrent's rule, a plain PathPrefix. All four crutches
gone. It is also the only chart without httpRoute.hostnames - it has no
ingress.domain either, and a chart-global hostname list could not say which of
the ten apps it belonged to, so the hostnames stay in ingress.host and
<app>.ingress.host.

CI: one render overlay per chart under ci/httproute/, so the assertion is not
vacuum-green - ci/run-httproute-match-test.sh goes from 20 held assertions over
10 charts to 36 over 15. freesailarr's overlay moves bazarr onto a host of its
own, so the shared-host route AND the per-app route are both rendered; without
that the per-app branch would be exercised by nothing. The install-test fixtures
stay in Ingress mode: the install runs one configuration per chart, cyberchef
already pays that price against the real Gateway-API CRDs, and for paperless-ngx
that fixture is the only install of the valkey-subchart path anywhere.

README also carries a stale figure fixed here: the HTTPRoute test was still
documented at the pilot's 4 assertions over 2 charts, although group 1 had taken
it to 20 over 10.

Not proven, by construction: that traffic flows. No Gateway controller runs in
CI. Not run locally either: k3d, Docker, Trivy. The Gateway branch of NOTES.txt
is EXECUTED by the render test (a template error in it fails the job, verified)
but its text is printed by no pipeline stage - helm template does not emit
notes, and the install-test installs in Ingress mode.

Part of #114
jklein approved these changes 2026-10-12 20:34:05 +02:00
Author
Member

CI is green: all 20 jobs of run 742 succeeded — Detect changed charts, the four Build jobs with their version-bump checks, Resource quantities, Selector match, HTTPRoute match, the four Trivy config jobs, and all five install-tests (calibre-web-automated, freesailarr, outline, paperless-ngx, zipline).

The HTTPRoute match job's own tail (job 2402), which is the figure README and the wiki now carry:

checked:       15 - bentopdf calibre-web-automated cyberchef freesailarr garage outline
                    paperless-ngx patchmon rallly searxng sure template-chart
                    urlaubsverwaltung voucher-vault zipline
not exercised: 15 - apache-tika bulwark gotenberg homeassistant keycloak matrix-synapse
                    minecraft-router minecraft-server rss-bridge samba satisfactory
                    stalwart teamspeak-server valkey ytdlp-web-player
failed:        0
assertions held: 36

That matches the local run exactly, which it should — the job is a pure render over every chart with no cluster involved. Group 1 left it at 20 assertions over 10 charts; the five new overlays add 16, eight of them from freesailarr alone (its own-host route plus the five rules of the shared route, qBittorrent's URLRewrite rule included).

not exercised is the honest half of that table. The fifteen charts listed there render no HTTPRoute at all: six of them are group 3, one is the frozen rss-bridge, and the rest have no Ingress to convert. Nothing in this PR claims coverage for them.

What the green install-tests do and do not say. They say the five charts install into a k3d cluster in Ingress mode and every workload becomes Ready — freesailarr including the real Wireguard tunnel and all ten containers, paperless-ngx including the bundled Valkey. They say nothing about the Gateway mode: no route is installed in any of them, because the install runs one configuration per chart and the Ingress was the better one to keep here. The route's schema validity is covered by cyberchef's fixture against the real CRDs; that it points somewhere by the render test above; that traffic flows by nothing, and by construction — no Gateway controller runs in CI.

CI is green: **all 20 jobs of run 742 succeeded** — `Detect changed charts`, the four `Build` jobs with their version-bump checks, `Resource quantities`, `Selector match`, `HTTPRoute match`, the four `Trivy config` jobs, and all five install-tests (`calibre-web-automated`, `freesailarr`, `outline`, `paperless-ngx`, `zipline`). The `HTTPRoute match` job's own tail (job 2402), which is the figure README and the wiki now carry: ``` checked: 15 - bentopdf calibre-web-automated cyberchef freesailarr garage outline paperless-ngx patchmon rallly searxng sure template-chart urlaubsverwaltung voucher-vault zipline not exercised: 15 - apache-tika bulwark gotenberg homeassistant keycloak matrix-synapse minecraft-router minecraft-server rss-bridge samba satisfactory stalwart teamspeak-server valkey ytdlp-web-player failed: 0 assertions held: 36 ``` That matches the local run exactly, which it should — the job is a pure render over every chart with no cluster involved. Group 1 left it at 20 assertions over 10 charts; the five new overlays add 16, eight of them from `freesailarr` alone (its own-host route plus the five rules of the shared route, qBittorrent's `URLRewrite` rule included). **`not exercised` is the honest half of that table.** The fifteen charts listed there render no `HTTPRoute` at all: six of them are group 3, one is the frozen `rss-bridge`, and the rest have no Ingress to convert. Nothing in this PR claims coverage for them. **What the green install-tests do and do not say.** They say the five charts install into a k3d cluster in **Ingress** mode and every workload becomes Ready — `freesailarr` including the real Wireguard tunnel and all ten containers, `paperless-ngx` including the bundled Valkey. They say nothing about the Gateway mode: no route is installed in any of them, because the install runs one configuration per chart and the Ingress was the better one to keep here. The route's schema validity is covered by `cyberchef`'s fixture against the real CRDs; that it *points somewhere* by the render test above; that traffic *flows* by nothing, and by construction — no Gateway controller runs in CI.
jklein added 1 commit 2026-10-12 20:36:59 +02:00
docs(README): the remaining-charts count, without double-counting rss-bridge
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 15s
Validate Helm Charts / Selector match (pull_request) Successful in 13s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 14s
Validate Helm Charts / Build calibre-web-automated (pull_request) Successful in 7s
Validate Helm Charts / Trivy config calibre-web-automated (pull_request) Successful in 20s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 8s
Validate Helm Charts / Build zipline (pull_request) Successful in 8s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 20s
Validate Helm Charts / Trivy config zipline (pull_request) Successful in 22s
Validate Helm Charts / Build outline (pull_request) Successful in 8s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 8s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 23s
Validate Helm Charts / Trivy config outline (pull_request) Successful in 16s
Install-test / Install-test calibre-web-automated (pull_request) Successful in 2m22s
Install-test / Install-test freesailarr (pull_request) Successful in 4m2s
Install-test / Install-test outline (pull_request) Successful in 2m50s
Install-test / Install-test paperless-ngx (pull_request) Successful in 3m45s
Install-test / Install-test zipline (pull_request) Successful in 2m23s
Validate Helm Charts / Install-test (pull_request) Successful in 15m22s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 14s
Publish Calibre Web Automated Helm Chart / Publish Calibre Web Automated Helm Chart (push) Successful in 14s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 14s
Publish Freesailarr Helm Chart / Publish Freesailarr Helm Chart (push) Successful in 14s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 12s
Publish Outline Helm Chart / Publish Outline Helm Chart (push) Successful in 12s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 11s
Publish Paperless-NGX Helm Chart / Publish Paperless-NGX Helm Chart (push) Successful in 11s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 10s
Publish Zipline Helm Chart / Publish Zipline Helm Chart (push) Successful in 10s
SonarQube Analysis / SonarQube Analysis (push) Successful in 44s
471ea5ba5d
Group 2's sweep paragraph said "seven charts plus rss-bridge" while rss-bridge
is one of those seven. Six charts in group 3 plus the frozen one is seven, which
is also what the twelve before this PR was counting.

Part of #114
jklein merged commit 471ea5ba5d into main 2026-10-12 21:16:22 +02:00
jklein deleted branch feat/gateway-api-group2 2026-10-12 21:16:23 +02:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jlab-cluster/helm-charts#117