wordpress 1.0.0 — WordPress on a shared volumeset with bundled MariaDB - #527
Open
jacobecox wants to merge 5 commits into
Open
wordpress 1.0.0 — WordPress on a shared volumeset with bundled MariaDB#527jacobecox wants to merge 5 commits into
jacobecox wants to merge 5 commits into
Conversation
WordPress 1.0.0: Apache + mod_php on a `shared` (read-write-many) volumeset, with a bundled MariaDB 11 via the `mariadb` 1.4.1 subchart. The admin account is created from a required prerequisite dictionary secret BEFORE Apache binds, so /wp-admin/install.php is never reachable and public access can default on. Build-stage probes (all five spec [UNPROVEN] items settled): - A `standard` workload MAY mount a `shared` volumeset (reached ready: true). - The shared volumeset is JuiceFS, mounted drwxrwxrwx root:root, and chown WORKS on it. The spec's `--no-same-owner` mitigation was harmful: it leaves root-owned dirs www-data cannot write into. Seed uses --owner www-data. - No filesystemGroupId needed (mount is world-writable, container runs root). - wp_install() driven from CLI verified end to end, with login + a wrong-password negative control, and the first-boot-only claim measured across a restart. - curl present in the image; wget is not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…opagation figure Two review findings, no blockers. 1. templates/workload.yaml concatenated the user's internalAccess.workloads with this release's own workload links, so a user who listed the release's own workload (the natural thing to do, since the list is the only place that relationship is visible) got a duplicate array entry — a drift candidate against what the API stores. The own-workload links are now emitted first and a user-supplied link that matches one is skipped, matching calcom.internalFirewall and documenso's workload. Self-inclusion behaviour is unchanged; only the duplicate is gone. 2. The firewall propagation figure said "~30 seconds to several minutes" in the README and "~30 s to a few minutes" in values.yaml. The catalog's measured outer bound is 559 s (9m19s, cockroach), and "several minutes" invites exactly the "this knob is broken" conclusion the note exists to prevent. Both now read "~30 s to ~10 min", matching calcom, documenso and this template's own briefing, which already carried the correct figure. Verified: the default `helm template --set global.cpln.gvc=test-gvc` render is byte-identical to before (the dedupe only affects workload-list); a list naming the release's own workload renders it once, and the conditional own-workload entries (phpmyadmin, maria-backup) still appear when those subchart features are on; all six README YAML blocks are still byte-identical substrings of values.yaml; lint clean.
The key was emitted only under `workload-list`, so the DEFAULT `same-gvc` install sent a PARTIAL `internal` block and the API completed it -- drift from creation, on the shape most users get. grafana-multi-location 2.0.0 shipped exactly this and it is worth recording why it survived review there: a `helm upgrade` drift gate CANNOT see this class. It reported `Unchanged` for both affected workloads the entire time the render and the stored spec disagreed, because the field is backfilled once at creation and then never churns. Only render-vs-stored finds it. Both arms verified: `same-gvc` now emits `[]`, and `workload-list` still emits this release's own workloads with user duplicates collapsed. Note the bundled mariadb subchart's own workload has the same gap. That is the `mariadb` template's defect and affects every chart vendoring it, so it is not fixed here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… make the port-forward path work
Test round 2026-08-31 (36 PASS / 2 FAIL) plus a coordinator finding. Four fixes,
each against a measurement, not an inspection.
1. render-vs-stored FAIL (gate requires 0 differences; 15 measured, 4 ours).
`templates/workload.yaml` sent a PARTIAL `firewallConfig.external` block, so
the API completed it with `inboundBlockedCIDR`, `outboundBlockedCIDR` and
`outboundAllowHostname` as `[]`, and an undeclared `supportDynamicTags`
stored as `false` — four differences from creation on release test-wp. All
four are now declared (shape copied from clickhouse 3.0.0). `outboundAllowPort`
is deliberately still absent: the API did NOT backfill it, and declaring a
field it does not store would create the drift this removes.
The two no-op `helm upgrade`s were fully green throughout — this class is
written once at creation and never churns, which is why both gates exist.
The other 11 differences belong to the vendored `mariadb` 1.4.1 workload and
cannot be fixed from a parent chart; recorded in the briefing instead.
2. No structural location confinement. The workload rendered a bare
`minScale: {{ .Values.wordpress.replicas }}` with no `localOptions`, so it ran
in EVERY location the GVC had and the boot-time guard was the only thing
between a user and a second site — and the guard is best-effort (below).
Adds a top-level `location` value with `defaultOptions.minScale/maxScale: 0`
plus a `localOptions` entry carrying the real count (pgedge 2.0.0 /
cockroach 2.0.0 / plane 1.0.0 shape), so an undeclared location starts nothing
by construction. The bundled MariaDB still cannot be pinned by a parent and
still runs everywhere — said plainly rather than claimed solved.
3. The guard is best-effort and now says so. It failed open on 1 boot in 5
(`WARNING: could not read GVC … guard SKIPPED`) and passed during a
location-removal drain, which let aws-us-east-2 replicas seed and run a second
complete independent WordPress install (east-1: 7 posts incl. the test post and
its attachment; east-2: the bare 3-post fresh-install set). README, values
comments and briefing now describe a safety net, not a guarantee. The guard is
kept — it covers the unpinned MariaDB and the opposite direction — and it now
logs the HTTP status, so a 403, a timeout and an unparseable 200 are no longer
one identical line. Also drops the new `mktemp` dependency for `$$` paths.
4. The README's port-forward instruction stranded the user. Run verbatim at
`publicAccess: false`: `/` → 301 to `http://localhost/`, `/wp-admin/` → 302 to
an unresolvable `*.cpln.local`, `curl -L` → final=000; only `/cpln-health.php`
answered. The tunnel was fine (200 through it while the canonical endpoint
returned 403) — WordPress rewrites from its stored site URL. Now paired with
`wordpress.siteUrl: http://localhost:8080` on the same port (measured: front
page 200, login 302, wp-admin 200), with the port agreement made explicit.
5. The ~95–107 s first-upgrade outage is the bundled MariaDB, not the web tier.
A WordPress-only roll was transparent at both replica counts (200/200 and
300/300, each with a control proving the replicas were replaced), so
`wordpress.replicas` does not mitigate it. The Availability section said the
opposite by implication; corrected.
Verified without deploying: default render clean (14 resources, no other values);
all four fields present on both publicAccess arms and the workload-list arm;
localOptions carries exactly defaultOptions' field set plus `location`; the boot
guard's branches exercised offline (1-loc ok / 2-loc fresh FATAL / 2-loc
initialised warn / pinned-location mismatch / fail-open) and `bash -n` clean;
12 existing validation negatives and 4 positive controls unchanged; 3 new
`location` negatives reject; every README YAML block byte-identical to
values.yaml; template-lint clean.
Needs a deploy to settle: whether the API stores `localOptions` verbatim (the
render-vs-stored gate must be re-run), and the guard's HTTP-status arm against
the real $CPLN_ENDPOINT — a local stub can only prove the parsing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The link-form check caught '//location/x' but not a comma-separated list
('aws-us-east-1,aws-us-east-2'), which Helm and the API both accept and
which stores a placement matching no real location -- nothing runs anywhere
and the logs are silent. Found on documenso in round 2; the same class
applied here.
One regex closes every arm: link form, comma list, uppercase, whitespace.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WordPress on Apache + mod_php, on a shared (read-write-many) volumeset at
/var/www/html, with a bundled MariaDB 11.Testing
Two rounds on
test-gvcandtest-gvc-2. Round 1: 36 PASS / 2 FAIL. Round 2 after fixes: 13 PASS / 0 FAIL, run deliberately on a three-location GVC so the placement behaviour could actually be exercised.Round 1's two failures, both now closed with measurements:
render-vs-storedgate. The workload sent a partialfirewallConfig.externalblock, so the API backfilled three fields, and it never declaredsupportDynamicTags. Nowok workload/…with 0 differences. Deliberately did not declareoutboundAllowPort— the measured diff shows the API does not backfill it on this workload, and declaring a field it does not store would recreate the drift the fix removes./returned301tohttp://localhost/,/wp-admin/302'd to an unresolvable*.cpln.local, andcurl -Lended atfinal=000. The tunnel was fine — the site URL was not. Pairing the port-forward withwordpress.siteUrl: http://localhost:8080inverts every one of those:200,200, and an authed dashboard. The "set it back before going public" step restores public browsing in 35 s, and skipping it genuinely breaks the site (control run).Also fixed: the chart had no structural location confinement, so it relied entirely on a runtime guard that round 1 measured failing open on 1 boot in 5. It now ships
defaultOptions.minScale/maxScale: 0with alocalOptionsentry; on the 3-location GVC the two non-configured locations reportThis workload location is deactivated because maxScale is set to 0.localOptionsis stored verbatim — rendered and stored are field-for-field identical apart from the standard link normalization. That was the one place this fix could have introduced new drift, and it did not.Known and stated honestly, not claimed solved
The bundled
mariadbsubchart cannot be pinned by a parent, so in a multi-location GVC MariaDB still starts in every location with its own volume — measured ready in all three within 47 s, genuinely independent instances. The README, values comments and briefing all say the protection is two-layer and best-effort rather than a guarantee, and carry the measured 1-in-5 fail-open and the drain-window hole.Eleven
render-vs-storeddifferences remain inside that subchart. They reproduced byte-for-byte across two different GVCs and region sets, so they are deterministic subchart behaviour, not environmental — same class as the nine inpostgres3.4.1.The ~95–107 s outage on the first
helm upgradeis the bundled MariaDB, not the web tier: a WordPress-only roll is transparent at both replica counts (200/200 and 300/300, each with a control proving replicas were actually replaced).wordpress.replicasdoes not mitigate it, and the README no longer implies it does.For maintainer judgment
The guard hard-refuses a fresh install into a multi-location GVC, which is correct given the MariaDB split — but the user experience is a crash-looping workload whose only diagnostic is
cpln logs. Worth deciding whether that should be softer, better-signposted, or conditional on whether a bundled database is in use.🤖 Generated with Claude Code