Skip to content

test(docker): cover read-only root startup and persistent migration state - #8325

Open
Bart-Core wants to merge 2 commits into
ether:developfrom
Bart-Core:codex/etherpad-readonly-startup
Open

Bart-Core wants to merge 2 commits into
ether:developfrom
Bart-Core:codex/etherpad-readonly-startup

Conversation

@Bart-Core

Copy link
Copy Markdown

Related to #8084. This is regression coverage and deployment documentation, not
a claim that this PR fixes all read-only plugin deployments.

I reproduced the reported first-boot failure with the official 3.3.3 image:
pnpm 11.9.0's ls --long --json --depth=0 --no-production exits 1. A diagnostic
preload traced the failing SQLite open to:
/home/etherpad/.local/share/pnpm/store/v11/index.db
Direct invocation returns ERR_SQLITE_ERROR: unable to open database file.
This is the package-manager store, not the Etherpad SQLite database in var/.

The official 3.3.7 image (pnpm 12.8.1) already boots with the same read-only
core-only layout. No runtime code change is proposed. The repository-pinned
pnpm 12.4.2 native binary also lists the installed packages successfully in an
offline, read-only probe; this probe is not a full develop-image build.

Changes:

  • Add an opt-in, dependency-free host-side Node/Docker regression test.
  • Run it against the image already built by the existing Docker CI job.
  • Document writable runtime mounts and explicitly limit the example to core-only
    deployments; no blanket promise for third-party or migrated legacy plugins.

The test uses UID 5001, read-only root, dropped capabilities, no-new-privileges,
network=none, no published ports, and bounded resources. It verifies initial
HTTP health/home, migration-state creation, and restart using the same var volume
without another migration. Only its uniquely named test container and volume are
removed. It does not install dependencies or pull images.

Local validation, same final test source:

  • etherpad/etherpad:3.3.3: FAIL at the reported pnpm migration step (~11.6s).
  • etherpad/etherpad:3.3.7: PASS for startup/restart/state persistence (~32.3s).
  • node --check and git diff --check: passed.

Image digests:
3.3.3 sha256:6020e7b57f4b17389ef000a5a2b04e6ab3ea6659c7c7ab83b81587010fa58be4
3.3.7 sha256:6f87beef31d91e4a44c7e3fe64043dd294f70443a80267a77267bdac704819a1

Separate limitation: 3.3.7's shutdown watchdog exits 1 after HTTP/database shutdown,
on BOTH writable and read-only roots in this environment. The test reports that
code, rejects OOM/forced-kill stops, and checks that restart succeeds; it does NOT
claim clean process shutdown. Original failed clean-shutdown check was retained
in the local evidence. No first-fixed-release bisection, full develop build,
collaborative editing test, plugin migration qualification, or full suite claimed.

AI-assisted contribution; assertions above refer to actual local container runs.

…tate

Add an isolated opt-in container regression and document core-only writable mounts. Preserve readonly and non-root restrictions; make no runtime change. Report shutdown exit status separately from startup and restart evidence.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Test read-only Docker startup and persistent migration state

🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Add regression coverage for core-only startup with a read-only root filesystem and persistent
 migration state.
• Run the test against the production image already built in Docker CI.
• Document required writable mounts and clarify that plugin deployments and clean shutdown are not
 covered.
Diagram

graph TD
  CI["Docker CI"] --> T["Host regression"] --> C["Read-only container"] --> H["HTTP checks"] --> R["Container restart"] --> P["Migration assertions"] --> V[("Var volume")]
  C --> V
  R --> C
Loading
High-Level Assessment

Using the existing production-image CI job and a host-side Node test is appropriate: it exercises the deployment boundary without changing runtime behavior, installing dependencies, or pulling an image. An inline CI shell test would make local reproduction harder; a new container-test framework would add unnecessary dependencies. Keep the stated core-only and shutdown limitations explicit.

Files changed (4) +172 / -0

Documentation (1) +53 / -0
docker.mdDocument core-only read-only deployment +53/-0

Document core-only read-only deployment

• Provides a UID 5001 Docker example with writable tmpfs mounts and a persistent var volume. Explains plugin and settings limitations, the observed version-specific startup behavior, and why the test does not establish clean shutdown.

doc/docker.md

Other (3) +119 / -0
docker.ymlRun the read-only regression in Docker CI +6/-0

Run the read-only regression in Docker CI

• Adds a step to the existing production-image build job that runs the Node regression against its locally built test tag.

.github/workflows/docker.yml

package.jsonExpose an opt-in read-only container test command +1/-0

Expose an opt-in read-only container test command

• Adds a script that runs the host-side regression with Node's built-in test runner.

package.json

readonly-root.mjsVerify read-only first boot and migration-state persistence +112/-0

Verify read-only first boot and migration-state persistence

• Creates an isolated, resource-bounded Docker container with a read-only root and persistent var volume. Checks HTTP startup and migration-state creation, then restarts and verifies that the state remains unchanged and migration does not repeat. Reports shutdown status separately and removes only test-created resources.

src/tests/container/readonly-root.mjs

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Failed boots can leave test volumes behind ✓ Resolved
Description
The finally block calls the asserting docker() wrapper for docker stop before attempting
either resource removal. If startup fails or the container exits before becoming healthy and that
stop returns a nonzero status, cleanup replaces the startup failure with a stop error and skips
removal of both the container and its volume.
Code

src/tests/container/readonly-root.mjs[R106-110]

+    if (containerCreated) {
+      docker('stop', '--time', '15', name);
+      docker('rm', name);
+    }
+    if (volumeCreated) docker('volume', 'rm', volume);
Evidence
The helper asserts that every Docker command exits zero. The test creates the container before
starting it and explicitly fails when it observes a container that has exited before health; its
cleanup then calls docker stop without handling an error before reaching either removal command.

src/tests/container/readonly-root.mjs[10-16]
src/tests/container/readonly-root.mjs[28-36]
src/tests/container/readonly-root.mjs[61-79]
src/tests/container/readonly-root.mjs[104-110]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A failed `docker stop` during test cleanup throws before the test can remove its container or volume, and can mask the original startup failure.
## Fix Focus Areas
- src/tests/container/readonly-root.mjs[104-110]
- src/tests/container/readonly-root.mjs[10-16]
## Recommended Fix
Make cleanup tolerate a container that is already stopped, and attempt container and volume removal independently even if stopping or removing the container fails. Preserve the original test failure while reporting any cleanup failure.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can copy the agent prompt from any finding and feed it to your IDE agent

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/tests/container/readonly-root.mjs Outdated

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant