From 6b0bc18b1833fce44fbf8bbebeaef579bc7bb8e2 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 15:28:31 +0200 Subject: [PATCH 01/11] =?UTF-8?q?fix(review):=20#138=20f2=20=E2=80=94=20Be?= =?UTF-8?q?rekende=20primaire=20host=20niet=20herkend=20als=20dubbel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228417 validate_extra_hostnames vergeleek alleen met een expliciete tenant.hostname. Nieuwe helper primary_host() berekent de primaire host zoals de ApplicationSet (tenant.hostname, anders .commonground.nu / ..commonground.nu, inclusief het ontbreken van 'demo' als suffix in het template). Gecontroleerd tegen de gerenderde nextcloud.host van alle 89 tenants: gelijk. Fixture tenant-extra-hostnames-berekende-primaire-host (zonder hostname) faalt nu met 'bevat de primaire host'; tegen de PR-head-validator slaagde hij. Co-Authored-By: Claude Opus 5.5 --- nextcloud-platform/scripts/validate-values.sh | 43 +++++++++++++++++-- ...a-hostnames-berekende-primaire-host.expect | 1 + ...tra-hostnames-berekende-primaire-host.yaml | 11 +++++ 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.yaml diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index d95335a..78a0623 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -405,6 +405,35 @@ validate_hostname_convention() { return 0 } +# Primaire host zoals de ApplicationSet (argo/applicationsets/nextcloud-tenants.yaml) +# hem bepaalt: tenant.hostname, en anders afgeleid uit tenant.name. Houd dit +# gelijk met dat template. Let op: het template kent 'demo' niet als suffix, +# zo'n tenant valt terug op tenant.environment (bv. x-demo.accept.commonground.nu). +# +# Usage: primary_host +primary_host() { + local name="$1" env="$2" hostname="$3" suffix="" org="$1" omgeving + + if [ -n "$hostname" ] && [ "$hostname" != "null" ]; then + echo "$hostname" + return 0 + fi + + if [[ "$name" =~ (accept|test|prod)$ ]]; then + suffix="${BASH_REMATCH[1]}" + fi + if [[ "$name" =~ ^(.*)-(accept|test|prod)$ ]]; then + org="${BASH_REMATCH[1]}" + fi + omgeving="${suffix:-$env}" + + if [ "$omgeving" = "prod" ]; then + echo "${org}.commonground.nu" + else + echo "${org}.${omgeving}.commonground.nu" + fi +} + # Validate tenant.extraHostnames (optioneel) # # Extra hostnamen op dezelfde Nextcloud. De ApplicationSet zet ze in @@ -414,7 +443,7 @@ validate_hostname_convention() { # en een secretnaam die Kubernetes accepteert (DNS-subdomein, max 253). validate_extra_hostnames() { local file="$1" - local kind name primary + local kind name hostname primary primary_bron kind=$(yq eval '.tenant.extraHostnames | type' "$file" 2>/dev/null) if [ "$kind" = "!!null" ] || [ -z "$kind" ]; then @@ -427,7 +456,13 @@ validate_extra_hostnames() { fi name=$(yq eval '.tenant.name' "$file" 2>/dev/null) - primary=$(yq eval '.tenant.hostname // ""' "$file" 2>/dev/null) + hostname=$(yq eval '.tenant.hostname // ""' "$file" 2>/dev/null) + primary=$(primary_host "$name" "$(yq eval '.tenant.environment' "$file" 2>/dev/null)" "$hostname") + if [ -n "$hostname" ]; then + primary_bron="die staat al in tenant.hostname" + else + primary_bron="afgeleid uit tenant.name, zoals de ApplicationSet doet" + fi local has_error=0 host seen=" " secret while IFS= read -r host; do @@ -436,8 +471,8 @@ validate_extra_hostnames() { has_error=1 continue fi - if [ -n "$primary" ] && [ "$host" = "$primary" ]; then - log_error "$file: tenant.extraHostnames bevat de primaire host '$host' (die staat al in tenant.hostname)" + if [ "$host" = "$primary" ]; then + log_error "$file: tenant.extraHostnames bevat de primaire host '$host' ($primary_bron)" has_error=1 fi if [[ "$seen" == *" $host "* ]]; then diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.expect new file mode 100644 index 0000000..9aa6da7 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.expect @@ -0,0 +1 @@ +bevat de primaire host 'voorbeeld.commonground.nu' diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.yaml new file mode 100644 index 0000000..b864e8a --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-berekende-primaire-host.yaml @@ -0,0 +1,11 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + apps: + enabled: + - openregister + extraHostnames: + - voorbeeld.commonground.nu From 34c94c0bcadc8ab29a550eb2bfc944ab050f6fa5 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 15:43:44 +0200 Subject: [PATCH 02/11] =?UTF-8?q?fix(review):=20#138=20f1=20=E2=80=94=20Ex?= =?UTF-8?q?tra=20host=20mag=20de=20host=20van=20een=20andere=20tenant=20cl?= =?UTF-8?q?aimen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228400 validate-values.sh bouwt nu per run een host-register over alle tenantbestanden in values/tenants/ (primaire host zoals de ApplicationSet hem bepaalt, tenant.extraHostnames, tenant.frontend.host, tenant.frontend.extraHosts) en weigert elke host die twee tenants claimen (validate_host_claims). Een bestand op de commandoregel vervangt het tenantbestand met dezelfde tenant.name, dus ook `validate-values.sh <één bestand>` vergelijkt met de hele vloot en een gewijzigde kopie botst niet met zichzelf. De check werkt in beide richtingen: een nieuwe tenant B faalt ook als een bestaande A zijn host al als extra host heeft (getest met een tijdelijk tenantbestand). Een extra host onder commonground.nu moet volgens de naamconventie van validate_hostname_convention bij de eigen tenant horen (convention_hosts: .commonground.nu / .migrate.commonground.nu voor prod, ..commonground.nu anders). Geen nieuwe opt-in. Class sweep: binnen één tenant mag een extra host ook niet de host van de eigen WOO-frontend zijn (zelfde botsing, andere Ingress). Checklijsten bijgewerkt in de commentaarkop van validate_extra_hostnames, docs/CHECKS-AND-BALANCES.md en changelog.d/tenant-extra-hostnames.md; run-tests.sh noemt dat de botsingsfixtures op tenant-dinkelland-prod leunen (de runner kent alleen enkelvoudige cases; de validator leest values/tenants/ zelf, dus de runner hoefde niet te veranderen). Fixtures: andere-tenant (primaire host van dinkelland-prod), andere-frontend (frontend.host van dinkelland-prod), commonground-vreemd, commonground-eigen (PASS), eigen-frontend. De vier foute cases slagen tegen de PR-head-validator. Co-Authored-By: Claude Opus 5.5 --- changelog.d/tenant-extra-hostnames.md | 5 +- docs/CHECKS-AND-BALANCES.md | 7 +- nextcloud-platform/scripts/validate-values.sh | 114 +++++++++++++++++- ...ant-extra-hostnames-andere-frontend.expect | 1 + ...enant-extra-hostnames-andere-frontend.yaml | 13 ++ ...enant-extra-hostnames-andere-tenant.expect | 1 + .../tenant-extra-hostnames-andere-tenant.yaml | 13 ++ ...-extra-hostnames-commonground-eigen.expect | 1 + ...nt-extra-hostnames-commonground-eigen.yaml | 13 ++ ...extra-hostnames-commonground-vreemd.expect | 1 + ...t-extra-hostnames-commonground-vreemd.yaml | 13 ++ ...nant-extra-hostnames-eigen-frontend.expect | 1 + ...tenant-extra-hostnames-eigen-frontend.yaml | 15 +++ nextcloud-platform/tests/run-tests.sh | 4 + 14 files changed, 198 insertions(+), 4 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.yaml diff --git a/changelog.d/tenant-extra-hostnames.md b/changelog.d/tenant-extra-hostnames.md index fc4055d..c8f16b4 100644 --- a/changelog.d/tenant-extra-hostnames.md +++ b/changelog.d/tenant-extra-hostnames.md @@ -21,7 +21,10 @@ gerenderd (Argo-goTemplate met sprig en `missingkey=default`, daarna manifests per tenant byte voor byte gelijk. `validate-values.sh` controleert het nieuwe veld (lijst, geldige DNS-namen, -geen dubbele, niet de primaire host, secretnaam ≤ 253), met fixtures in +geen dubbele, niet de primaire host, onder `commonground.nu` alleen de eigen +namen, secretnaam ≤ 253) en weigert over alle tenantbestanden heen een host die +twee tenants claimen (primaire host, `extraHostnames`, `frontend.host`, +`frontend.extraHosts`), met fixtures in `nextcloud-platform/tests/cases/tenant-extra-hostnames-*`. Bekende beperkingen: `trusted_domains` wordt alleen bij de eerste installatie diff --git a/docs/CHECKS-AND-BALANCES.md b/docs/CHECKS-AND-BALANCES.md index 5bdd456..212c8d4 100644 --- a/docs/CHECKS-AND-BALANCES.md +++ b/docs/CHECKS-AND-BALANCES.md @@ -36,8 +36,11 @@ Before merge, CI runs `scripts/verify.sh`, which wraps: - `nextcloud-platform/scripts/smoke-checks.sh` — Helm chart renders `validate-values.sh` also checks the optional `tenant.extraHostnames` list (a -list, valid DNS names, no duplicates, not the primary host, a derived TLS secret -name Kubernetes accepts). A valid list still gives a warning, because DNS and +list, valid DNS names, no duplicates, not the primary host, under +`commonground.nu` only this tenant's own names, a derived TLS secret name +Kubernetes accepts). It also checks across all tenant files that no host +(primary, `extraHostnames`, `frontend.host`, `frontend.extraHosts`) is claimed +by two tenants, also when it is run on a single file. A valid list still gives a warning, because DNS and HTTP-01 for those hosts are outside what CI can see. Fixtures for every branch are in `nextcloud-platform/tests/cases/tenant-extra-hostnames-*`. diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index 78a0623..22133b9 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -440,7 +440,9 @@ primary_host() { # trustedDomains en rendert er een tweede Ingress voor, met één TLS-secret # "nextcloud---tls" per host. Gecontroleerd: # lijstvorm, geldige DNS-naam, geen dubbele, niet gelijk aan de primaire host, -# en een secretnaam die Kubernetes accepteert (DNS-subdomein, max 253). +# onder commonground.nu alleen hosts van de eigen tenant, en een secretnaam die +# Kubernetes accepteert (DNS-subdomein, max 253). Botsingen met hosts van andere +# tenants vangt validate_host_claims. validate_extra_hostnames() { local file="$1" local kind name hostname primary primary_bron @@ -475,6 +477,13 @@ validate_extra_hostnames() { log_error "$file: tenant.extraHostnames bevat de primaire host '$host' ($primary_bron)" has_error=1 fi + # commonground.nu is van het platform: een extra host daaronder moet + # volgens de naamconventie bij deze tenant horen. + if [ "$host" != "$primary" ] && [[ "$host" == commonground.nu || "$host" == *.commonground.nu ]] \ + && ! convention_hosts "$name" | grep -qxF -- "$host"; then + log_error "$file: tenant.extraHostnames bevat '$host' onder commonground.nu, maar die hoort niet bij tenant '$name' (toegestaan: $(convention_hosts "$name" | paste -sd ' '))" + has_error=1 + fi if [[ "$seen" == *" $host "* ]]; then log_error "$file: tenant.extraHostnames bevat '$host' dubbel" has_error=1 @@ -494,6 +503,105 @@ validate_extra_hostnames() { return $has_error } +# Hosts onder commonground.nu die volgens de conventie bij deze tenant horen. +# Zelfde regel als validate_hostname_convention: prod krijgt .commonground.nu +# en de migratiehost .migrate.commonground.nu, de rest +# ..commonground.nu. +# +# Usage: convention_hosts +convention_hosts() { + local name="$1" org suffix + [[ "$name" =~ ^(.+)-(accept|test|demo|prod)$ ]] || return 0 + org="${BASH_REMATCH[1]}" + suffix="${BASH_REMATCH[2]}" + if [ "$suffix" = "prod" ]; then + echo "${org}.commonground.nu" + echo "${org}.migrate.commonground.nu" + else + echo "${org}.${suffix}.commonground.nu" + fi +} + +# Alle hosts die één tenantbestand claimt, als regels "": +# de primaire host (zoals de ApplicationSet hem bepaalt), tenant.extraHostnames +# en de WOO-frontend (tenant.frontend.host en tenant.frontend.extraHosts, alleen +# als ze expliciet gezet zijn; de afgeleide frontend-host staat in React-base). +# +# Usage: tenant_host_claims +tenant_host_claims() { + local file="$1" name env hostname + name=$(yq eval '.tenant.name // ""' "$file" 2>/dev/null) || return 0 + [ -z "$name" ] && return 0 + env=$(yq eval '.tenant.environment // ""' "$file" 2>/dev/null) + hostname=$(yq eval '.tenant.hostname // ""' "$file" 2>/dev/null) + printf '%s\tprimaire host\n' "$(primary_host "$name" "$env" "$hostname")" + local bron host + while read -r bron host; do + [ -n "$host" ] && printf '%s\t%s\n' "$host" "$bron" + done < <(yq eval '.tenant as $t | ( + [$t.extraHostnames // [] | select(tag == "!!seq") | .[] | to_string | "tenant.extraHostnames " + .] + + [$t.frontend.host // null | select(. != null) | to_string | "tenant.frontend.host " + .] + + [$t.frontend.extraHosts // [] | select(tag == "!!seq") | .[] | to_string | "tenant.frontend.extraHosts " + .] + ) | .[]' "$file" 2>/dev/null || true) +} + +# Host-register over ALLE tenants, als regels "". +# Bron: elk bestand in values/tenants/, plus de bestanden op de commandoregel. +# Een bestand op de commandoregel vervangt het tenantbestand met dezelfde +# tenant.name, zodat een gewijzigde kopie niet met zichzelf botst. Zo vergelijkt +# ook `validate-values.sh <één bestand>` met de rest van de vloot. +HOST_CLAIMS="" +build_host_claims() { + local -A by_name=() + local file name + while IFS= read -r -d '' file; do + name=$(yq eval '.tenant.name // ""' "$file" 2>/dev/null) || continue + [ -n "$name" ] && by_name["$name"]="$file" + done < <(find "$TENANT_DIR" -name "tenant-*.yaml" -print0 2>/dev/null) + for file in "$@"; do + name=$(yq eval '.tenant.name // ""' "$file" 2>/dev/null) || continue + [ -n "$name" ] && by_name["$name"]="$file" + done + + local host bron + for name in "${!by_name[@]}"; do + while IFS=$'\t' read -r host bron; do + [ -z "$host" ] && continue + HOST_CLAIMS+="${host}"$'\t'"${name}"$'\t'"${bron}"$'\n' + done < <(tenant_host_claims "${by_name[$name]}") + done +} + +# Geen host mag door twee tenants geclaimd worden. Een tweede Ingress voor +# dezelfde host (in een andere namespace) wordt door de admission-webhook van +# ingress-nginx geweigerd, of zonder webhook samengevoegd met die van de ander: +# dan gaat verkeer naar de verkeerde Nextcloud. Binnen één tenant mag een extra +# host ook niet de host van de eigen WOO-frontend zijn (een andere Ingress). +validate_host_claims() { + local file="$1" name + name=$(yq eval '.tenant.name // ""' "$file" 2>/dev/null) || return 0 + [ -z "$name" ] && return 0 + + local own has_error=0 host bron other + own=$(tenant_host_claims "$file") + while IFS=$'\t' read -r host bron; do + [ -z "$host" ] && continue + while IFS=$'\t' read -r other; do + [ -z "$other" ] && continue + log_error "$file: host '$host' ($bron) wordt al geclaimd door tenant ${other}" + has_error=1 + done < <(printf '%s' "$HOST_CLAIMS" | awk -F'\t' -v h="$host" -v n="$name" \ + '$1 == h && $2 != n { print "\x27" $2 "\x27 (" $3 ")" }') + if [ "$bron" = "tenant.extraHostnames" ] \ + && printf '%s\n' "$own" | grep -qxF -e "${host}"$'\t'"tenant.frontend.host" -e "${host}"$'\t'"tenant.frontend.extraHosts"; then + log_error "$file: tenant.extraHostnames bevat '$host', de host van de eigen WOO-frontend" + has_error=1 + fi + done <<<"$own" + + return $has_error +} + # Validate wave number validate_wave() { local file="$1" @@ -730,6 +838,7 @@ validate_tenant_file() { validate_hostname "$file" || ((file_errors++)) validate_hostname_convention "$file" || ((file_errors++)) validate_extra_hostnames "$file" || ((file_errors++)) + validate_host_claims "$file" || ((file_errors++)) validate_wave "$file" || ((file_errors++)) validate_bucket "$file" || ((file_errors++)) validate_frontend_image "$file" || ((file_errors++)) @@ -764,6 +873,9 @@ main() { exit 0 fi + # Hosts van alle tenants, voor validate_host_claims + build_host_claims "$@" + echo "==========================================" echo "Validating ${#files[@]} tenant file(s)" echo "==========================================" diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.expect new file mode 100644 index 0000000..34f86a2 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.expect @@ -0,0 +1 @@ +host 'open.dinkelland.nl' (tenant.extraHostnames) wordt al geclaimd door tenant 'dinkelland-prod' (tenant.frontend.host) diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml new file mode 100644 index 0000000..0f4cb18 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - open.dinkelland.nl diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.expect new file mode 100644 index 0000000..dad078f --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.expect @@ -0,0 +1 @@ +host 'dinkelland.commonground.nu' (tenant.extraHostnames) wordt al geclaimd door tenant 'dinkelland-prod' (primaire host) diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml new file mode 100644 index 0000000..a211b8c --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - dinkelland.commonground.nu diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.expect new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.expect @@ -0,0 +1 @@ +PASS diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.yaml new file mode 100644 index 0000000..cf7d17b --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-eigen.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - voorbeeld.commonground.nu diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.expect new file mode 100644 index 0000000..f906cd9 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.expect @@ -0,0 +1 @@ +bevat 'nergens.commonground.nu' onder commonground.nu, maar die hoort niet bij tenant 'voorbeeld-prod' diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.yaml new file mode 100644 index 0000000..93ef9f7 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-commonground-vreemd.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - nergens.commonground.nu diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.expect new file mode 100644 index 0000000..8a80912 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.expect @@ -0,0 +1 @@ +tenant.extraHostnames bevat 'open.voorbeeld.nl', de host van de eigen WOO-frontend diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.yaml new file mode 100644 index 0000000..b99f661 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-eigen-frontend.yaml @@ -0,0 +1,15 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + frontend: + host: open.voorbeeld.nl + extraHostnames: + - open.voorbeeld.nl diff --git a/nextcloud-platform/tests/run-tests.sh b/nextcloud-platform/tests/run-tests.sh index e2f0177..04912d4 100755 --- a/nextcloud-platform/tests/run-tests.sh +++ b/nextcloud-platform/tests/run-tests.sh @@ -21,6 +21,10 @@ # Een fixture die per ongeluk op een ANDERE check faalt dan bedoeld wordt zo # ook gevangen: de verwachte deelstring komt dan niet voor. # +# De validator vergelijkt hosts met alle tenantbestanden in values/tenants/, +# ook bij één bestand. Fixtures die een botsing met een andere tenant toetsen +# (tenant-extra-hostnames-andere-*) leunen daarom op tenant-dinkelland-prod. +# # Writes: read-only (schrijft alleen in een eigen mktemp-map) # Idempotent: ja # Requires: bash, yq (via validate-values.sh) From 13687d421935afbfabe5b6cd51e63231f3ed7252 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 15:45:59 +0200 Subject: [PATCH 03/11] =?UTF-8?q?fix(review):=20#138=20f5=20=E2=80=94=20Se?= =?UTF-8?q?cretnamen=20botsen=20bij=20a-b.nl=20en=20a.b.nl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228465 validate_extra_hostnames weigert nu een afgeleide TLS-secretnaam die al bij een andere extra host hoort (punten worden streepjes, dus a-b.nl en a.b.nl gaven hetzelfde secret). Een dubbele host meldt alleen 'dubbel' en slaat de overige checks over, zodat hij niet ook als secretbotsing telt. Fixture tenant-extra-hostnames-zelfde-secret; slaagde tegen de PR-head-validator. Class sweep: de checklijst in docs/CHECKS-AND-BALANCES.md, changelog.d/tenant-extra-hostnames.md en de commentaarkop noemen nu een unieke secretnaam. De secretnaam van de hoofd-Ingress (nextcloud--tls) kan niet met een afgeleide naam botsen (die heeft altijd een host-deel). Co-Authored-By: Claude Opus 5.5 --- changelog.d/tenant-extra-hostnames.md | 2 +- docs/CHECKS-AND-BALANCES.md | 4 ++-- nextcloud-platform/scripts/validate-values.sh | 15 ++++++++++++--- .../tenant-extra-hostnames-zelfde-secret.expect | 1 + .../tenant-extra-hostnames-zelfde-secret.yaml | 14 ++++++++++++++ 5 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.yaml diff --git a/changelog.d/tenant-extra-hostnames.md b/changelog.d/tenant-extra-hostnames.md index c8f16b4..accde9b 100644 --- a/changelog.d/tenant-extra-hostnames.md +++ b/changelog.d/tenant-extra-hostnames.md @@ -22,7 +22,7 @@ manifests per tenant byte voor byte gelijk. `validate-values.sh` controleert het nieuwe veld (lijst, geldige DNS-namen, geen dubbele, niet de primaire host, onder `commonground.nu` alleen de eigen -namen, secretnaam ≤ 253) en weigert over alle tenantbestanden heen een host die +namen, unieke secretnaam ≤ 253) en weigert over alle tenantbestanden heen een host die twee tenants claimen (primaire host, `extraHostnames`, `frontend.host`, `frontend.extraHosts`), met fixtures in `nextcloud-platform/tests/cases/tenant-extra-hostnames-*`. diff --git a/docs/CHECKS-AND-BALANCES.md b/docs/CHECKS-AND-BALANCES.md index 212c8d4..4cbba7c 100644 --- a/docs/CHECKS-AND-BALANCES.md +++ b/docs/CHECKS-AND-BALANCES.md @@ -37,8 +37,8 @@ Before merge, CI runs `scripts/verify.sh`, which wraps: `validate-values.sh` also checks the optional `tenant.extraHostnames` list (a list, valid DNS names, no duplicates, not the primary host, under -`commonground.nu` only this tenant's own names, a derived TLS secret name -Kubernetes accepts). It also checks across all tenant files that no host +`commonground.nu` only this tenant's own names, a unique derived TLS secret +name Kubernetes accepts). It also checks across all tenant files that no host (primary, `extraHostnames`, `frontend.host`, `frontend.extraHosts`) is claimed by two tenants, also when it is run on a single file. A valid list still gives a warning, because DNS and HTTP-01 for those hosts are outside what CI can see. Fixtures for every branch diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index 22133b9..48f6553 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -440,8 +440,8 @@ primary_host() { # trustedDomains en rendert er een tweede Ingress voor, met één TLS-secret # "nextcloud---tls" per host. Gecontroleerd: # lijstvorm, geldige DNS-naam, geen dubbele, niet gelijk aan de primaire host, -# onder commonground.nu alleen hosts van de eigen tenant, en een secretnaam die -# Kubernetes accepteert (DNS-subdomein, max 253). Botsingen met hosts van andere +# onder commonground.nu alleen hosts van de eigen tenant, en een unieke +# secretnaam die Kubernetes accepteert (DNS-subdomein, max 253). Botsingen met hosts van andere # tenants vangt validate_host_claims. validate_extra_hostnames() { local file="$1" @@ -466,7 +466,7 @@ validate_extra_hostnames() { primary_bron="afgeleid uit tenant.name, zoals de ApplicationSet doet" fi - local has_error=0 host seen=" " secret + local has_error=0 host seen=" " secret seen_secrets=" " while IFS= read -r host; do if ! [[ "$host" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ ]]; then log_error "$file: tenant.extraHostnames bevat ongeldige hostnaam '$host'" @@ -487,6 +487,8 @@ validate_extra_hostnames() { if [[ "$seen" == *" $host "* ]]; then log_error "$file: tenant.extraHostnames bevat '$host' dubbel" has_error=1 + # De eerste keer zijn de overige checks voor deze host al gedaan + continue fi seen="${seen}${host} " secret="nextcloud-${name}-${host//./-}-tls" @@ -494,6 +496,13 @@ validate_extra_hostnames() { log_error "$file: TLS-secretnaam '$secret' voor '$host' is langer dan 253 tekens" has_error=1 fi + # Punten worden streepjes, dus a-b.nl en a.b.nl krijgen hetzelfde + # secret: één Certificate dat maar één van beide hosts dekt. + if [[ "$seen_secrets" == *" $secret "* ]]; then + log_error "$file: TLS-secretnaam '$secret' voor '$host' is gelijk aan die van een andere extra host" + has_error=1 + fi + seen_secrets="${seen_secrets}${secret} " done < <(yq eval '.tenant.extraHostnames[]' "$file" 2>/dev/null) if [ $has_error -eq 0 ]; then diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.expect new file mode 100644 index 0000000..656bd5e --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.expect @@ -0,0 +1 @@ +TLS-secretnaam 'nextcloud-voorbeeld-prod-a-b-voorbeeld-nl-tls' voor 'a.b.voorbeeld.nl' is gelijk aan die van een andere extra host diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.yaml new file mode 100644 index 0000000..f7dd437 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zelfde-secret.yaml @@ -0,0 +1,14 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - a-b.voorbeeld.nl + - a.b.voorbeeld.nl From 71e97bcd0382ec446b792b3436f674a212182c03 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 15:51:00 +0200 Subject: [PATCH 04/11] =?UTF-8?q?fix(review):=20#138=20f6=20=E2=80=94=20Ho?= =?UTF-8?q?stnaam-regex=20accepteert=20localhost,=20123=20en=20labels=20>6?= =?UTF-8?q?3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228475 Nieuwe helper is_valid_hostname: RFC 1123-labels zoals voorheen, plus minstens één punt, elk label max 63 en het geheel max 253 tekens. validate_extra_hostnames gebruikt hem; de melding noemt de regels. Class sweep: validate_hostname (tenant.hostname) gebruikte dezelfde regex en had hetzelfde gat; ook die gebruikt nu is_valid_hostname. De 89 tenants blijven groen. Fixtures: tenant-extra-hostnames-localhost, -getal (123), -label-te-lang (label van 64 tekens) en tenant-hostname-zonder-punt; alle vier slaagden tegen de PR-head-validator. Een host > 253 werd voor extraHostnames al via de secretnaamlengte gevangen, dus daar geen aparte fixture. Co-Authored-By: Claude Opus 5.5 --- nextcloud-platform/scripts/validate-values.sh | 25 ++++++++++++++++--- .../cases/tenant-extra-hostnames-getal.expect | 1 + .../cases/tenant-extra-hostnames-getal.yaml | 13 ++++++++++ ...enant-extra-hostnames-label-te-lang.expect | 1 + .../tenant-extra-hostnames-label-te-lang.yaml | 13 ++++++++++ .../tenant-extra-hostnames-localhost.expect | 1 + .../tenant-extra-hostnames-localhost.yaml | 13 ++++++++++ .../cases/tenant-hostname-zonder-punt.expect | 1 + .../cases/tenant-hostname-zonder-punt.yaml | 11 ++++++++ 9 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.expect create mode 100644 nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.yaml diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index 48f6553..45cb75e 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -331,6 +331,23 @@ validate_chart_version_format() { return 0 } +# Hostnaam zoals Kubernetes hem in een Ingress-rule accepteert (RFC 1123): +# labels van kleine letters, cijfers en koppeltekens, minstens één punt, elk +# label max 63 tekens en het geheel max 253. Zonder punt of met een te lang +# label weigert de API-server de Ingress en faalt de sync van de Application. +# +# Usage: is_valid_hostname +is_valid_hostname() { + local host="$1" label + [[ "$host" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)+$ ]] || return 1 + [ ${#host} -le 253 ] || return 1 + local IFS=. + for label in $host; do + [ ${#label} -le 63 ] || return 1 + done + return 0 +} + # Validate hostname format validate_hostname() { local file="$1" @@ -343,8 +360,8 @@ validate_hostname() { fi # Basic hostname validation (RFC 1123) - if ! [[ "$hostname" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ ]]; then - log_error "$file: Invalid hostname '$hostname'. Must be a valid DNS name." + if ! is_valid_hostname "$hostname"; then + log_error "$file: Invalid hostname '$hostname'. Must be a valid DNS name (at least one dot, labels max 63, total max 253)." return 1 fi @@ -468,8 +485,8 @@ validate_extra_hostnames() { local has_error=0 host seen=" " secret seen_secrets=" " while IFS= read -r host; do - if ! [[ "$host" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ ]]; then - log_error "$file: tenant.extraHostnames bevat ongeldige hostnaam '$host'" + if ! is_valid_hostname "$host"; then + log_error "$file: tenant.extraHostnames bevat ongeldige hostnaam '$host' (kleine letters, minstens één punt, labels max 63, totaal max 253)" has_error=1 continue fi diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.expect new file mode 100644 index 0000000..aceaeda --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.expect @@ -0,0 +1 @@ +tenant.extraHostnames bevat ongeldige hostnaam '123' diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.yaml new file mode 100644 index 0000000..13163e7 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-getal.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - 123 diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.expect new file mode 100644 index 0000000..abcb929 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.expect @@ -0,0 +1 @@ +tenant.extraHostnames bevat ongeldige hostnaam 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.voorbeeld.nl' diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.yaml new file mode 100644 index 0000000..0f2cbc0 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-label-te-lang.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.voorbeeld.nl diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.expect new file mode 100644 index 0000000..7ad51c5 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.expect @@ -0,0 +1 @@ +tenant.extraHostnames bevat ongeldige hostnaam 'localhost' diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.yaml new file mode 100644 index 0000000..45670d4 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-localhost.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - localhost diff --git a/nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.expect b/nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.expect new file mode 100644 index 0000000..fd7d8a8 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.expect @@ -0,0 +1 @@ +Invalid hostname 'localhost' diff --git a/nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.yaml b/nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.yaml new file mode 100644 index 0000000..b4f76d2 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-hostname-zonder-punt.yaml @@ -0,0 +1,11 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: localhost + hostnameOverride: true + apps: + enabled: + - openregister From 50934c5007b4578bb5f0194821394ff2d9eab002 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 15:52:01 +0200 Subject: [PATCH 05/11] =?UTF-8?q?fix(review):=20#138=20f7=20=E2=80=94=20Ex?= =?UTF-8?q?tra=20Ingress=20negeert=20ingress.enabled=20en=20ingress.path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228481 De extra Ingress nextcloud-extra-hosts volgt nu templates/ingress.yaml van chart 8.9.0: hij staat achter `if .Values.ingress.enabled` en neemt ingress.path en ingress.pathType via de tpl over in plaats van vast `/` en `Prefix`. Class sweep (zelfde afwijking van de chart-template): annotations en ingressClassName staan nu ook achter `with`, zoals in de chart, zodat een lege waarde geen `annotations: {}` of lege `ingressClassName:` oplevert. Docs-tabel in docs/ADDING-TENANT.md en changelog.d/tenant-extra-hostnames.md bijgewerkt. Gerenderd met helm (dinkelland-prod + extraHostnames, chart 8.9.0): met de standaardwaarden byte-identiek aan de PR-head; ingress.enabled=false geeft geen enkele Ingress meer (PR-head rendert dan nog nextcloud-extra-hosts); ingress.path=/x en pathType=ImplementationSpecific komen in beide Ingresses. Co-Authored-By: Claude Opus 5.5 --- changelog.d/tenant-extra-hostnames.md | 6 ++--- docs/ADDING-TENANT.md | 2 +- .../applicationsets/nextcloud-tenants.yaml | 25 ++++++++++++------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/changelog.d/tenant-extra-hostnames.md b/changelog.d/tenant-extra-hostnames.md index accde9b..303e66a 100644 --- a/changelog.d/tenant-extra-hostnames.md +++ b/changelog.d/tenant-extra-hostnames.md @@ -10,9 +10,9 @@ Een tenant kan nu op meer dan één hostnaam antwoorden. Nieuw optioneel veld (`ingress.hosts` uit de ApplicationSet leest die chart niet); - geeft elke extra host een eigen TLS-blok en dus een eigen Certificate via HTTP-01, zodat één host met nog niet kloppende DNS de andere niet tegenhoudt; -- neemt annotaties, `ingressClassName` en poort via de `tpl` van de chart over - uit dezelfde values als de hoofd-Ingress: geen tweede kopie van de - nginx-instellingen. +- neemt `ingress.enabled`, annotaties, `ingressClassName`, `path`, `pathType` + en poort via de `tpl` van de chart over uit dezelfde values als de + hoofd-Ingress: geen tweede kopie van de nginx-instellingen. **Bestaande tenants renderen byte-identiek.** Het blok wordt alleen geëmit als de lijst gezet is. Gemeten: alle 89 tenantbestanden op main en op deze branch diff --git a/docs/ADDING-TENANT.md b/docs/ADDING-TENANT.md index feb6ead..1ca89ac 100644 --- a/docs/ADDING-TENANT.md +++ b/docs/ADDING-TENANT.md @@ -202,7 +202,7 @@ What the ApplicationSet does with it (`argo/applicationsets/nextcloud-tenants.ya | `trusted_domains` | every extra host is added to `nextcloud.trustedDomains`, after the primary host | | routing | a second Ingress `nextcloud-extra-hosts`, because chart 8.9.0 puts only `nextcloud.host` in its own Ingress rules | | certificates | one TLS entry per host, secret `nextcloud---tls`, so one cert-manager Certificate per host via HTTP-01 (`letsencrypt-prod`). A host whose DNS is not ready does not block the others | -| annotations | taken from `ingress.annotations`, `ingress.className` and `service.port` through the chart's `tpl`, so they always match the main Ingress | +| annotations | taken from `ingress.annotations`, `ingress.className`, `ingress.path`, `ingress.pathType` and `service.port` through the chart's `tpl`, so they always match the main Ingress; with `ingress.enabled: false` neither Ingress is rendered | A tenant without `extraHostnames` renders byte-identical to before; the block is only emitted when the list is set. diff --git a/nextcloud-platform/argo/applicationsets/nextcloud-tenants.yaml b/nextcloud-platform/argo/applicationsets/nextcloud-tenants.yaml index 6dc3b9f..e34d15f 100644 --- a/nextcloud-platform/argo/applicationsets/nextcloud-tenants.yaml +++ b/nextcloud-platform/argo/applicationsets/nextcloud-tenants.yaml @@ -193,13 +193,15 @@ spec: # De chart (8.9.0) zet in zijn eigen Ingress alleen nextcloud.host # als rule, dus extra hosts krijgen hier een tweede Ingress. Eén # TLS-blok per host, dus één Certificate per host: een host waarvan - # DNS nog niet klopt houdt de andere niet tegen. Annotaties, - # ingressClassName en poort komen via de tpl van de chart uit - # dezelfde values als de hoofd-Ingress, dus geen tweede kopie van - # de nginx-instellingen. Alleen geëmit als de lijst gezet is: - # bestaande tenants renderen byte-identiek. + # DNS nog niet klopt houdt de andere niet tegen. ingress.enabled, + # annotaties, ingressClassName, path, pathType en poort komen via + # de tpl van de chart uit dezelfde values als de hoofd-Ingress, + # op dezelfde manier als templates/ingress.yaml van de chart, dus + # geen tweede kopie van de nginx-instellingen. Alleen geëmit als + # de lijst gezet is: bestaande tenants renderen byte-identiek. extraManifests: - | + {{`{{- if .Values.ingress.enabled }}`}} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: @@ -208,10 +210,14 @@ spec: app.kubernetes.io/name: nextcloud app.kubernetes.io/part-of: nextcloud-platform nextcloud.platform/tenant: "{{ $name }}" + {{`{{- with .Values.ingress.annotations }}`}} annotations: - {{`{{- toYaml .Values.ingress.annotations | nindent 4 }}`}} + {{`{{- toYaml . | nindent 4 }}`}} + {{`{{- end }}`}} spec: - ingressClassName: {{`{{ .Values.ingress.className }}`}} + {{`{{- with .Values.ingress.className }}`}} + ingressClassName: {{`{{ . }}`}} + {{`{{- end }}`}} tls: {{- range $extraHosts }} - secretName: "nextcloud-{{ $name }}-{{ replace "." "-" . }}-tls" @@ -223,14 +229,15 @@ spec: - host: "{{ . }}" http: paths: - - path: / - pathType: Prefix + - path: {{`{{ .Values.ingress.path }}`}} + pathType: {{`{{ .Values.ingress.pathType }}`}} backend: service: name: nextcloud port: number: {{`{{ .Values.service.port }}`}} {{- end }} + {{`{{- end }}`}} {{- end }} # Our GitOps repo for values files From 72bb9e384b906face16506696dbcb37dc4282bd1 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 15:53:37 +0200 Subject: [PATCH 06/11] =?UTF-8?q?docs(review):=20#138=20f8=20=E2=80=94=20H?= =?UTF-8?q?ost=20verwijderen=20blijft=20in=20trusted=5Fdomains=20staan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228492 docs/ADDING-TENANT.md: een host uit extraHostnames halen prunet de Ingress, maar de host blijft in config.php tot `occ config:system:delete trusted_domains `. Eerst `occ config:system:get trusted_domains` draaien om de juiste te vinden, want `set` op een bezette index overschrijft die entry. Class sweep: dezelfde beperking staat in changelog.d/tenant-extra-hostnames.md en noemt nu ook het verwijderen. Co-Authored-By: Claude Opus 5.5 --- changelog.d/tenant-extra-hostnames.md | 4 +++- docs/ADDING-TENANT.md | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/changelog.d/tenant-extra-hostnames.md b/changelog.d/tenant-extra-hostnames.md index 303e66a..668f0b0 100644 --- a/changelog.d/tenant-extra-hostnames.md +++ b/changelog.d/tenant-extra-hostnames.md @@ -28,5 +28,7 @@ twee tenants claimen (primaire host, `extraHostnames`, `frontend.host`, `nextcloud-platform/tests/cases/tenant-extra-hostnames-*`. Bekende beperkingen: `trusted_domains` wordt alleen bij de eerste installatie -geschreven; `charts/tenant-httproute` neemt nog één host. Zie +geschreven, en een verwijderde host blijft erin staan tot een +`occ config:system:delete trusted_domains `; `charts/tenant-httproute` neemt +nog één host. Zie `docs/ADDING-TENANT.md`. diff --git a/docs/ADDING-TENANT.md b/docs/ADDING-TENANT.md index 1ca89ac..3256b4f 100644 --- a/docs/ADDING-TENANT.md +++ b/docs/ADDING-TENANT.md @@ -220,7 +220,11 @@ Four things to know: reads `NEXTCLOUD_TRUSTED_DOMAINS` when it installs; adding a host to a running tenant later also needs `occ config:system:set trusted_domains --value=` in the pod (same pattern as `cutover-tenant.sh`). Put the full list in the file - before the tenant is first created. + before the tenant is first created. Removing a host from the list works the + same way: Argo prunes the Ingress, but the host stays in `config.php` until + you run `occ config:system:delete trusted_domains `. In both cases run + `occ config:system:get trusted_domains` first to find the right ``: `set` + on an index that is already in use overwrites that entry. - **Gateway API routes do not use this list.** `charts/tenant-httproute` takes one host; a tenant with `gateway.nextcloud: true` gets a route for the primary host only. From bf29d92078e48d0ffc71692aafd82fdbc647a752 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 16:14:53 +0200 Subject: [PATCH 07/11] =?UTF-8?q?fix(review):=20#138=20f7=20=E2=80=94=20pr?= =?UTF-8?q?e-push:=20extraHostnames=20vereist=20ingress.enabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228481 Sinds f7 volgt nextcloud-extra-hosts ingress.enabled, net als de chart. Met ingress.enabled: false (de Gateway-cutover) hadden de extra hosts dan geen enkele route, want charts/tenant-httproute neemt alleen de primaire host. validate_extra_hostnames weigert nu extraHostnames samen met een effectieve ingress.enabled die niet true is. effective_ingress_enabled leest de waarde in dezelfde valueFiles-volgorde als de ApplicationSet (common, env, db, canary-overrides als tenant.canary, tenantbestand; de inline values zetten hem niet). Alle 89 tenants: true, gelijk aan de renders (89 met Ingress). Fixture tenant-extra-hostnames-zonder-ingress; met de check weggehaald slaagt hij (mutatie gecontroleerd). docs/GATEWAY-API.md (extraHostnames-alinea en de cutover-alinea) en docs/ADDING-TENANT.md zeggen het nu. Co-Authored-By: Claude Opus 5.5 --- docs/ADDING-TENANT.md | 5 +++- docs/GATEWAY-API.md | 11 +++++-- nextcloud-platform/scripts/validate-values.sh | 29 +++++++++++++++++++ ...nant-extra-hostnames-zonder-ingress.expect | 1 + ...tenant-extra-hostnames-zonder-ingress.yaml | 15 ++++++++++ 5 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.yaml diff --git a/docs/ADDING-TENANT.md b/docs/ADDING-TENANT.md index 3256b4f..f939ea6 100644 --- a/docs/ADDING-TENANT.md +++ b/docs/ADDING-TENANT.md @@ -227,7 +227,10 @@ Four things to know: on an index that is already in use overwrites that entry. - **Gateway API routes do not use this list.** `charts/tenant-httproute` takes one host; a tenant with `gateway.nextcloud: true` gets a route for the primary host - only. + only. The extra hosts stay on the nginx Ingress, and that Ingress follows + `ingress.enabled` like the chart's own: `ingress.enabled: false` would leave + them without any route, so `validate-values.sh` refuses `extraHostnames` + together with `ingress.enabled: false`. Validate before pushing: diff --git a/docs/GATEWAY-API.md b/docs/GATEWAY-API.md index 554ba1b..6c1b456 100644 --- a/docs/GATEWAY-API.md +++ b/docs/GATEWAY-API.md @@ -50,7 +50,10 @@ blok dat altijd meegaat zou alle 84 Applications tegelijk laten hersyncen. `tenant.extraHostnames` (zie `docs/ADDING-TENANT.md`) gaat hier niet mee: de route krijgt alleen de primaire host. Een tenant met extra hostnamen blijft voor die hosts op de nginx-Ingress tot deze chart een lijst hostnames aanneemt, met -per host een listener op de Gateway. +per host een listener op de Gateway. Die Ingress (`nextcloud-extra-hosts`) volgt +`ingress.enabled`, net als de hoofd-Ingress van de chart. `validate-values.sh` +weigert daarom `extraHostnames` samen met `ingress.enabled: false`: de extra +hosts zouden dan geen enkele route meer hebben. ## Waarom dit nog niet schaalt @@ -94,8 +97,10 @@ De HTTPRoute komt náást de Ingress. Het verkeer verschuift **niet**: het DNS-record blijft naar ingress-nginx wijzen, want external-dns laat een bestaand record met rust zolang de Ingress bestaat. Gemeten 2026-08-17. -De cutover is dus het weghalen van de Ingress. Doe dat niet lichtvaardig: het -certificaat van deze host wordt via HTTP-01 over díé Ingress vernieuwd. Verdwijnt +De cutover is dus het weghalen van de Ingress. Heeft de tenant +`tenant.extraHostnames`, dan kan dat nog niet: `ingress.enabled: false` haalt ook +de Ingress van de extra hosts weg, en de validator weigert die combinatie. Doe +het ook verder niet lichtvaardig: het certificaat van deze host wordt via HTTP-01 over díé Ingress vernieuwd. Verdwijnt hij voordat cert-manager's `--enable-gateway-api` aanstaat (die vlag staat nu uit), dan breekt de vernieuwing stil en pas bij de eerstvolgende renewal. diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index 45cb75e..9f7ca13 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -451,6 +451,27 @@ primary_host() { fi } +# ingress.enabled zoals de render hem ziet: dezelfde valueFiles-volgorde als de +# ApplicationSet (common, env, db, canary-overrides, tenantbestand), de laatste +# die hem zet wint. De inline values van de ApplicationSet zetten hem niet. +# +# Usage: effective_ingress_enabled +effective_ingress_enabled() { + local file="$1" values="${REPO_ROOT}/values" env db canary value f result="false" + env=$(yq eval '.tenant.environment // ""' "$file" 2>/dev/null) + db=$(yq eval '.tenant.dbType // "postgres"' "$file" 2>/dev/null) + canary=$(yq eval '.tenant.canary // ""' "$file" 2>/dev/null) + local layers=("${values}/common.yaml" "${values}/env/${env}.yaml" "${values}/db/${db}.yaml") + [ "$canary" = "true" ] && layers+=("${values}/canary-overrides.yaml") + layers+=("$file") + for f in "${layers[@]}"; do + [ -f "$f" ] || continue + value=$(yq eval '.ingress.enabled' "$f" 2>/dev/null) + [ "$value" != "null" ] && [ -n "$value" ] && result="$value" + done + echo "$result" +} + # Validate tenant.extraHostnames (optioneel) # # Extra hostnamen op dezelfde Nextcloud. De ApplicationSet zet ze in @@ -526,6 +547,14 @@ validate_extra_hostnames() { log_warning "$file: tenant.extraHostnames gezet; DNS en HTTP-01 per host moeten kloppen (zie docs/ADDING-TENANT.md)" fi + # De extra Ingress volgt ingress.enabled, net als die van de chart. Zonder + # Ingress hebben de extra hosts geen enkele route: charts/tenant-httproute + # neemt alleen de primaire host. + if [ "$(effective_ingress_enabled "$file")" != "true" ]; then + log_error "$file: tenant.extraHostnames vereist ingress.enabled: true. De extra hosts lopen via de nginx-Ingress zolang charts/tenant-httproute maar één host neemt (zie docs/GATEWAY-API.md)" + has_error=1 + fi + return $has_error } diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.expect new file mode 100644 index 0000000..09497b3 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.expect @@ -0,0 +1 @@ +tenant.extraHostnames vereist ingress.enabled: true diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.yaml new file mode 100644 index 0000000..c83cde7 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-zonder-ingress.yaml @@ -0,0 +1,15 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - www.voorbeeld.nl +ingress: + enabled: false From ab064c71051d9502bb4dc7a093a663ed3465c766 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 16:21:48 +0200 Subject: [PATCH 08/11] =?UTF-8?q?fix(review):=20#138=20f1=20=E2=80=94=20pr?= =?UTF-8?q?e-push:=20dubbele=20tenant.name=20en=20register=20=C3=A9=C3=A9n?= =?UTF-8?q?=20keer=20bouwen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228400 Dubbele tenant.name: build_host_claims hield één bestand per naam aan, dus een tweede tenantbestand met dezelfde naam viel stil uit de botsingscheck. dump_host_claims (het register van values/tenants/) geeft nu een error voor elk tweede bestand daar met een bestaande naam. Alleen een bestand BUITEN values/tenants/ (fixture, gewijzigde kopie) vervangt de tenant met dezelfde naam; een bestand binnen values/tenants/ wordt nooit als vervanger gebruikt, dus de omweg bestaat niet meer. Twee bestanden buiten de map met dezelfde naam zijn ook een error. Getest met een tijdelijke kopie in values/tenants/ (error, daarna verwijderd) en met fixture tenant-dubbele-naam, die een eigen vloot heeft in tenant-dubbele-naam.fleet/ (run-tests.sh zet daarvoor VALIDATE_TENANT_DIR). Met de dup-check weggehaald wordt die fixture rood. Looptijd: run-tests.sh bouwde per case het register over 89 bestanden opnieuw (~2,8 s per case). De validator accepteert nu VALIDATE_HOST_CLAIMS_FILE (geschreven met `validate-values.sh --dump-host-claims`); run-tests.sh bouwt het één keer. Ontbreekt het bestand, dan bouwt de validator het register zelf. Eén fixture: 2,8 s -> 0,3 s; hele suite (34 cases): ~14 s. Co-Authored-By: Claude Opus 5.5 --- nextcloud-platform/scripts/validate-values.sh | 94 ++++++++++++++++--- .../tests/cases/tenant-dubbele-naam.expect | 1 + .../tenant-zelfde-prod-a.yaml | 9 ++ .../tenant-zelfde-prod-b.yaml | 9 ++ .../tests/cases/tenant-dubbele-naam.yaml | 14 +++ nextcloud-platform/tests/run-tests.sh | 22 ++++- 6 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-dubbele-naam.expect create mode 100644 nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-a.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-b.yaml create mode 100644 nextcloud-platform/tests/cases/tenant-dubbele-naam.yaml diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index 9f7ca13..040eeca 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -11,12 +11,14 @@ # Usage: # ./scripts/validate-values.sh [tenant-file.yaml ...] # ./scripts/validate-values.sh # validates all tenants +# ./scripts/validate-values.sh --dump-host-claims # host-register (zie build_host_claims) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" -TENANT_DIR="${REPO_ROOT}/values/tenants" +# VALIDATE_TENANT_DIR: alleen voor tests/run-tests.sh (fixtures met een eigen vloot) +TENANT_DIR="${VALIDATE_TENANT_DIR:-${REPO_ROOT}/values/tenants}" # Colors for output RED='\033[0;31m' @@ -600,30 +602,85 @@ tenant_host_claims() { ) | .[]' "$file" 2>/dev/null || true) } -# Host-register over ALLE tenants, als regels "". -# Bron: elk bestand in values/tenants/, plus de bestanden op de commandoregel. -# Een bestand op de commandoregel vervangt het tenantbestand met dezelfde -# tenant.name, zodat een gewijzigde kopie niet met zichzelf botst. Zo vergelijkt -# ook `validate-values.sh <één bestand>` met de rest van de vloot. -HOST_CLAIMS="" -build_host_claims() { +# Absoluut pad met symlinks opgelost, zonder GNU realpath +abs_path() { + local dir + dir=$(cd "$(dirname "$1")" 2>/dev/null && pwd -P) || { echo "$1"; return 0; } + echo "${dir}/$(basename "$1")" +} + +# Host-register van values/tenants/, als regels "". +# Twee bestanden met dezelfde tenant.name zijn een fout: het register (en +# daarmee de botsingscheck) zou er stil één van overslaan. +dump_host_claims() { local -A by_name=() - local file name + local file name has_error=0 host bron while IFS= read -r -d '' file; do name=$(yq eval '.tenant.name // ""' "$file" 2>/dev/null) || continue - [ -n "$name" ] && by_name["$name"]="$file" - done < <(find "$TENANT_DIR" -name "tenant-*.yaml" -print0 2>/dev/null) + [ -z "$name" ] && continue + if [ -n "${by_name[$name]:-}" ]; then + log_error "$file: tenant.name '$name' staat ook in ${by_name[$name]}" + has_error=$((has_error + 1)) + continue + fi + by_name["$name"]="$file" + done < <(find "$TENANT_DIR" -name "tenant-*.yaml" -print0 2>/dev/null | sort -z) + + for name in "${!by_name[@]}"; do + while IFS=$'\t' read -r host bron; do + [ -z "$host" ] && continue + printf '%s\t%s\t%s\n' "$host" "$name" "$bron" + done < <(tenant_host_claims "${by_name[$name]}") + done + return $has_error +} + +# Host-register over ALLE tenants: dat van values/tenants/, plus de bestanden op +# de commandoregel. Een bestand BUITEN values/tenants/ (een fixture, een +# gewijzigde kopie) vervangt het tenantbestand met dezelfde tenant.name, zodat +# het niet met zichzelf botst. Zo vergelijkt ook `validate-values.sh <één +# bestand>` met de rest van de vloot. Een bestand BINNEN values/tenants/ staat +# al in het register; een tweede daar met een bestaande naam is een fout. +# +# VALIDATE_HOST_CLAIMS_FILE: een register dat `validate-values.sh +# --dump-host-claims` eerder schreef (run-tests.sh bouwt het één keer). Ontbreekt +# het bestand, dan wordt het register gewoon opnieuw opgebouwd. +HOST_CLAIMS="" +build_host_claims() { + local cache="${VALIDATE_HOST_CLAIMS_FILE:-}" + if [ -n "$cache" ] && [ -f "$cache" ]; then + HOST_CLAIMS=$(cat "$cache") + else + # Draait in een subshell: tel de fouten hier bij (de meldingen zelf + # staan al op stderr) + local dups=0 + HOST_CLAIMS=$(dump_host_claims) || dups=$? + ERRORS=$((ERRORS + dups)) + fi + [ -n "$HOST_CLAIMS" ] && HOST_CLAIMS+=$'\n' + + local tenant_dir file path name host bron + tenant_dir=$(cd "$TENANT_DIR" 2>/dev/null && pwd -P) || tenant_dir="$TENANT_DIR" + local -A overlay=() for file in "$@"; do + path=$(abs_path "$file") + [[ "$path" == "${tenant_dir}/"* ]] && continue name=$(yq eval '.tenant.name // ""' "$file" 2>/dev/null) || continue - [ -n "$name" ] && by_name["$name"]="$file" + [ -z "$name" ] && continue + if [ -n "${overlay[$name]:-}" ] && [ "${overlay[$name]}" != "$path" ]; then + log_error "$file: tenant.name '$name' staat ook in ${overlay[$name]}" + continue + fi + overlay["$name"]="$path" done - local host bron - for name in "${!by_name[@]}"; do + for name in "${!overlay[@]}"; do + HOST_CLAIMS=$(printf '%s' "$HOST_CLAIMS" | awk -F'\t' -v n="$name" '$2 != n') + [ -n "$HOST_CLAIMS" ] && HOST_CLAIMS+=$'\n' while IFS=$'\t' read -r host bron; do [ -z "$host" ] && continue HOST_CLAIMS+="${host}"$'\t'"${name}"$'\t'"${bron}"$'\n' - done < <(tenant_host_claims "${by_name[$name]}") + done < <(tenant_host_claims "${overlay[$name]}") done } @@ -911,6 +968,13 @@ validate_tenant_file() { # Main main() { check_dependencies + + # --dump-host-claims: schrijf het host-register van values/tenants/ naar + # stdout, voor VALIDATE_HOST_CLAIMS_FILE (zie build_host_claims) + if [ "${1:-}" = "--dump-host-claims" ]; then + dump_host_claims + exit $? + fi local files=() diff --git a/nextcloud-platform/tests/cases/tenant-dubbele-naam.expect b/nextcloud-platform/tests/cases/tenant-dubbele-naam.expect new file mode 100644 index 0000000..d3b4a95 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-dubbele-naam.expect @@ -0,0 +1 @@ +tenant.name 'zelfde-prod' staat ook in diff --git a/nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-a.yaml b/nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-a.yaml new file mode 100644 index 0000000..b5de65e --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-a.yaml @@ -0,0 +1,9 @@ +--- +tenant: + name: zelfde-prod + environment: prod + wave: "1" + dbType: postgres + apps: + enabled: + - openregister diff --git a/nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-b.yaml b/nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-b.yaml new file mode 100644 index 0000000..b5de65e --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-dubbele-naam.fleet/tenant-zelfde-prod-b.yaml @@ -0,0 +1,9 @@ +--- +tenant: + name: zelfde-prod + environment: prod + wave: "1" + dbType: postgres + apps: + enabled: + - openregister diff --git a/nextcloud-platform/tests/cases/tenant-dubbele-naam.yaml b/nextcloud-platform/tests/cases/tenant-dubbele-naam.yaml new file mode 100644 index 0000000..f0fa171 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-dubbele-naam.yaml @@ -0,0 +1,14 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - voorbeeld.nl + - www.voorbeeld.nl diff --git a/nextcloud-platform/tests/run-tests.sh b/nextcloud-platform/tests/run-tests.sh index 04912d4..219fead 100755 --- a/nextcloud-platform/tests/run-tests.sh +++ b/nextcloud-platform/tests/run-tests.sh @@ -24,6 +24,12 @@ # De validator vergelijkt hosts met alle tenantbestanden in values/tenants/, # ook bij één bestand. Fixtures die een botsing met een andere tenant toetsen # (tenant-extra-hostnames-andere-*) leunen daarom op tenant-dinkelland-prod. +# Dat register bouwt deze runner één keer (VALIDATE_HOST_CLAIMS_FILE), niet per +# case. +# +# .fleet/ optioneel: een eigen map met tenantbestanden die voor deze +# case values/tenants/ vervangt (VALIDATE_TENANT_DIR), voor +# checks over de hele vloot, zoals een dubbele tenant.name. # # Writes: read-only (schrijft alleen in een eigen mktemp-map) # Idempotent: ja @@ -66,7 +72,11 @@ run_case() { # validate-values.sh geeft non-zero bij fouten; dat is hier data, geen crash. set +e - output="$(bash "$VALIDATOR" "$yaml" 2>&1)" + if [[ -d "${CASES_DIR}/${name}.fleet" ]]; then + output="$(VALIDATE_TENANT_DIR="${CASES_DIR}/${name}.fleet" bash "$VALIDATOR" "$yaml" 2>&1)" + else + output="$(VALIDATE_HOST_CLAIMS_FILE="$CLAIMS_FILE" bash "$VALIDATOR" "$yaml" 2>&1)" + fi rc=$? set -e # ANSI-kleuren eruit, anders matchen de deelstrings niet @@ -119,6 +129,16 @@ main() { exit 1 fi + # Host-register van values/tenants/ één keer opbouwen in plaats van per + # case. Globaal: de EXIT-trap draait buiten deze functie. + TMP_DIR="$(mktemp -d)" + trap 'rm -rf "$TMP_DIR"' EXIT + CLAIMS_FILE="${TMP_DIR}/host-claims.tsv" + if ! bash "$VALIDATOR" --dump-host-claims >"$CLAIMS_FILE"; then + echo "error: host-register van values/tenants/ niet op te bouwen" >&2 + exit 1 + fi + local yaml for yaml in "${CASES_DIR}"/*.yaml; do [[ -e "$yaml" ]] || continue From 6da1eaa47695b7328a7af65f82e4132ff447b1b8 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 16:22:54 +0200 Subject: [PATCH 09/11] =?UTF-8?q?fix(review):=20#138=20f6=20=E2=80=94=20pr?= =?UTF-8?q?e-push:=20IP-adres=20als=20hostnaam=20weigeren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228475 is_valid_hostname accepteerde 81.24.6.82; Kubernetes neemt geen IP-adres als Ingress-host. Een naam waarvan het laatste label alleen cijfers is, wordt nu geweigerd. Geldt via de helper voor zowel tenant.extraHostnames als tenant.hostname; de meldingen noemen het. Fixture tenant-extra-hostnames-ip-adres; tegen de validator van de vorige commit slaagt hij (mutatie gecontroleerd). De 89 tenants blijven groen. Co-Authored-By: Claude Opus 5.5 --- nextcloud-platform/scripts/validate-values.sh | 10 ++++++---- .../cases/tenant-extra-hostnames-ip-adres.expect | 1 + .../cases/tenant-extra-hostnames-ip-adres.yaml | 13 +++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.expect create mode 100644 nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.yaml diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index 040eeca..e9bb936 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -335,14 +335,16 @@ validate_chart_version_format() { # Hostnaam zoals Kubernetes hem in een Ingress-rule accepteert (RFC 1123): # labels van kleine letters, cijfers en koppeltekens, minstens één punt, elk -# label max 63 tekens en het geheel max 253. Zonder punt of met een te lang -# label weigert de API-server de Ingress en faalt de sync van de Application. +# label max 63 tekens en het geheel max 253, en het laatste label niet alleen +# cijfers (dan is het een IP-adres, dat Kubernetes niet als Ingress-host neemt). +# Anders weigert de API-server de Ingress en faalt de sync van de Application. # # Usage: is_valid_hostname is_valid_hostname() { local host="$1" label [[ "$host" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)+$ ]] || return 1 [ ${#host} -le 253 ] || return 1 + [[ "${host##*.}" =~ ^[0-9]+$ ]] && return 1 local IFS=. for label in $host; do [ ${#label} -le 63 ] || return 1 @@ -363,7 +365,7 @@ validate_hostname() { # Basic hostname validation (RFC 1123) if ! is_valid_hostname "$hostname"; then - log_error "$file: Invalid hostname '$hostname'. Must be a valid DNS name (at least one dot, labels max 63, total max 253)." + log_error "$file: Invalid hostname '$hostname'. Must be a valid DNS name (at least one dot, labels max 63, total max 253, not an IP address)." return 1 fi @@ -509,7 +511,7 @@ validate_extra_hostnames() { local has_error=0 host seen=" " secret seen_secrets=" " while IFS= read -r host; do if ! is_valid_hostname "$host"; then - log_error "$file: tenant.extraHostnames bevat ongeldige hostnaam '$host' (kleine letters, minstens één punt, labels max 63, totaal max 253)" + log_error "$file: tenant.extraHostnames bevat ongeldige hostnaam '$host' (kleine letters, minstens één punt, labels max 63, totaal max 253, geen IP-adres)" has_error=1 continue fi diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.expect b/nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.expect new file mode 100644 index 0000000..02f0ce5 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.expect @@ -0,0 +1 @@ +tenant.extraHostnames bevat ongeldige hostnaam '81.24.6.82' diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.yaml new file mode 100644 index 0000000..e731742 --- /dev/null +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-ip-adres.yaml @@ -0,0 +1,13 @@ +--- +tenant: + name: voorbeeld-prod + environment: prod + wave: "1" + dbType: postgres + hostname: cloud.voorbeeld.nl + hostnameOverride: true + apps: + enabled: + - openregister + extraHostnames: + - 81.24.6.82 From fd6c9c2191f39146c9f2d344ed83911cebf35bfe Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 16:22:58 +0200 Subject: [PATCH 10/11] =?UTF-8?q?test(review):=20#138=20f1=20=E2=80=94=20p?= =?UTF-8?q?re-push:=20afhankelijkheid=20van=20dinkelland=20in=20fixtures?= =?UTF-8?q?=20noemen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228400 De fixtures tenant-extra-hostnames-andere-tenant en -andere-frontend toetsen een botsing met tenant-dinkelland-prod. Een regel bovenin zegt nu dat ze breken als de hosts van dinkelland veranderen. Co-Authored-By: Claude Opus 5.5 --- .../tests/cases/tenant-extra-hostnames-andere-frontend.yaml | 1 + .../tests/cases/tenant-extra-hostnames-andere-tenant.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml index 0f4cb18..335da83 100644 --- a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-frontend.yaml @@ -1,4 +1,5 @@ --- +# Leunt op values/tenants/tenant-dinkelland-prod.yaml: breekt als de hosts van dinkelland veranderen. tenant: name: voorbeeld-prod environment: prod diff --git a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml index a211b8c..a41911d 100644 --- a/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml +++ b/nextcloud-platform/tests/cases/tenant-extra-hostnames-andere-tenant.yaml @@ -1,4 +1,5 @@ --- +# Leunt op values/tenants/tenant-dinkelland-prod.yaml: breekt als de hosts van dinkelland veranderen. tenant: name: voorbeeld-prod environment: prod From 86e8c7f7190e55396c8a5a2b3ac8e2dffcecd361 Mon Sep 17 00:00:00 2001 From: Robert Zondervan Date: Wed, 14 Oct 2026 16:36:08 +0200 Subject: [PATCH 11/11] =?UTF-8?q?docs(review):=20#138=20f1=20=E2=80=94=20p?= =?UTF-8?q?re-push:=20check=20lists=20bijgewerkt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/ConductionNL/Nextcloud-base/pull/138#discussion_r4271228400 De checklijsten noemen nu ook de checks uit de pre-push-ronde: extraHostnames niet samen met ingress.enabled: false, geen IP-adres als host (ook voor tenant.hostname), en geen dubbele tenant.name over tenantbestanden heen. Bijgewerkt in docs/CHECKS-AND-BALANCES.md, changelog.d/tenant-extra-hostnames.md en de commentaarkop van validate_extra_hostnames (git grep: geen andere lijsten). Co-Authored-By: Claude Opus 5.5 --- changelog.d/tenant-extra-hostnames.md | 12 +++++++----- docs/CHECKS-AND-BALANCES.md | 12 +++++++----- nextcloud-platform/scripts/validate-values.sh | 9 +++++---- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/changelog.d/tenant-extra-hostnames.md b/changelog.d/tenant-extra-hostnames.md index 668f0b0..a5de288 100644 --- a/changelog.d/tenant-extra-hostnames.md +++ b/changelog.d/tenant-extra-hostnames.md @@ -20,11 +20,13 @@ gerenderd (Argo-goTemplate met sprig en `missingkey=default`, daarna `helm template` met chart 8.9.0 en dezelfde valueFiles-volgorde), values en manifests per tenant byte voor byte gelijk. -`validate-values.sh` controleert het nieuwe veld (lijst, geldige DNS-namen, -geen dubbele, niet de primaire host, onder `commonground.nu` alleen de eigen -namen, unieke secretnaam ≤ 253) en weigert over alle tenantbestanden heen een host die -twee tenants claimen (primaire host, `extraHostnames`, `frontend.host`, -`frontend.extraHosts`), met fixtures in +`validate-values.sh` controleert het nieuwe veld (lijst, geldige DNS-namen en +geen IP-adres, geen dubbele, niet de primaire host, onder `commonground.nu` +alleen de eigen namen, unieke secretnaam ≤ 253, niet samen met +`ingress.enabled: false`). Ook `tenant.hostname` mag geen IP-adres meer zijn. +Over alle tenantbestanden heen weigert hij een host die twee tenants claimen +(primaire host, `extraHostnames`, `frontend.host`, `frontend.extraHosts`) en +een `tenant.name` die in twee bestanden staat, met fixtures in `nextcloud-platform/tests/cases/tenant-extra-hostnames-*`. Bekende beperkingen: `trusted_domains` wordt alleen bij de eerste installatie diff --git a/docs/CHECKS-AND-BALANCES.md b/docs/CHECKS-AND-BALANCES.md index 4cbba7c..b2ced81 100644 --- a/docs/CHECKS-AND-BALANCES.md +++ b/docs/CHECKS-AND-BALANCES.md @@ -36,11 +36,13 @@ Before merge, CI runs `scripts/verify.sh`, which wraps: - `nextcloud-platform/scripts/smoke-checks.sh` — Helm chart renders `validate-values.sh` also checks the optional `tenant.extraHostnames` list (a -list, valid DNS names, no duplicates, not the primary host, under -`commonground.nu` only this tenant's own names, a unique derived TLS secret -name Kubernetes accepts). It also checks across all tenant files that no host -(primary, `extraHostnames`, `frontend.host`, `frontend.extraHosts`) is claimed -by two tenants, also when it is run on a single file. A valid list still gives a warning, because DNS and +list, valid DNS names and no IP address, no duplicates, not the primary host, +under `commonground.nu` only this tenant's own names, a unique derived TLS +secret name Kubernetes accepts, and not together with `ingress.enabled: false`). +`tenant.hostname` is refused as an IP address too. It also checks across all +tenant files that no host (primary, `extraHostnames`, `frontend.host`, +`frontend.extraHosts`) is claimed by two tenants, also when it is run on a +single file, and that no two tenant files share a `tenant.name`. A valid list still gives a warning, because DNS and HTTP-01 for those hosts are outside what CI can see. Fixtures for every branch are in `nextcloud-platform/tests/cases/tenant-extra-hostnames-*`. diff --git a/nextcloud-platform/scripts/validate-values.sh b/nextcloud-platform/scripts/validate-values.sh index e9bb936..4ccab33 100644 --- a/nextcloud-platform/scripts/validate-values.sh +++ b/nextcloud-platform/scripts/validate-values.sh @@ -481,10 +481,11 @@ effective_ingress_enabled() { # Extra hostnamen op dezelfde Nextcloud. De ApplicationSet zet ze in # trustedDomains en rendert er een tweede Ingress voor, met één TLS-secret # "nextcloud---tls" per host. Gecontroleerd: -# lijstvorm, geldige DNS-naam, geen dubbele, niet gelijk aan de primaire host, -# onder commonground.nu alleen hosts van de eigen tenant, en een unieke -# secretnaam die Kubernetes accepteert (DNS-subdomein, max 253). Botsingen met hosts van andere -# tenants vangt validate_host_claims. +# lijstvorm, geldige DNS-naam (geen IP-adres), geen dubbele, niet gelijk aan de +# primaire host, onder commonground.nu alleen hosts van de eigen tenant, een +# unieke secretnaam die Kubernetes accepteert (DNS-subdomein, max 253), en +# ingress.enabled: true. Botsingen met hosts van andere tenants vangt +# validate_host_claims; een dubbele tenant.name vangt dump_host_claims. validate_extra_hostnames() { local file="$1" local kind name hostname primary primary_bron