Skip to content

feat: drain and patch individual CLI Docker pool slots - #269

Merged
drewstone merged 3 commits into
mainfrom
fix/pool-runtime-roll-ready
Oct 23, 2026
Merged

drewstone merged 3 commits into
mainfrom
fix/pool-runtime-roll-ready

Conversation

@drewstone

Copy link
Copy Markdown
Owner

Existing historical CLI Docker pools can retain a vulnerable runtime while their original bridge process is gone. Add bearer-protected adoption and individual slot drain/immutable-image roll to the existing bridge owner. Admission stops for that slot while its active lease finishes; other slots remain available. Adoption binds explicit container IDs and the original data-directory ownership lock, refusing listeners, execs or unexpected processes.

Replacement preserves existing credential volumes, binds, operator environment and resource settings, verifies the maintained Node pin and Docker preflight, and retains the stopped old container. Durable identity receipts allow interrupted transitions and owner restarts to reconcile safely. Extract provisioning from ContainerPool so the logic remains below1,000lines. No dependency or lockfile change.

Independent source review resolved per-start setup ordering, restart reconciliation and refused-adoption lock lifetime. Exact b54d9b8 passed the real Beelink2 Docker/authenticated HTTP journey and typecheck (gate beelink2-e0f9488daa3427f4d486b5316406cc62,189s): active exec survives drain; another slot serves; unexpected work blocks adoption without retaining an unused lock; volumes/environment/resources survive real replacements; the actual HTTP owner restarts and safely re-admits completed replacements. Fixture containers and volumes were removed through their test owner; no model calls. Earlier fail-closed Docker ps-format failures were retained and fixed by including Docker's required PID field and validating the parent/child process tree.

This source qualification does not claim the eight live pools have already changed image. Their normal source-only deploy and per-slot API rollout provide that production proof. Optional preflight caching is excluded from this proven head.

Run ID: run_4b1d54cfdf2c05412b39044bf98511e1

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-23T00:39:55.930534Z b54d9b8 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@drewstone
drewstone merged commit 3fe68c0 into main Oct 23, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b54d9b8d0b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


async function requireNoListener(port: number): Promise<void> {
await new Promise<void>((resolve, reject) => {
const socket = createConnection({ host: '127.0.0.1', port })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check every supported listener address before adopting

When a legacy controller is bound only to ::1 or a specific non-loopback interface, connecting exclusively to 127.0.0.1 returns ECONNREFUSED and adoption proceeds even though that controller is still listening. Both configurations are supported by BRIDGE_HOST; this is especially dangerous for older controllers that predate the data-directory lock, because the new owner may then stop and replace containers that the live controller is using. Probe the configured addresses or inspect listeners in an address-family-independent way before accepting ownership.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #270: bounded Linux ss queries every TCP listener address family/interface for the declared port and fail closed on unavailable or ambiguous evidence. The actual ::1-only listener fixture refused adoption, released its unused ownership lock, and allowed adoption after listener closure (Beelink2 gate 7e352c38ef5603efe6d580883b0f6ce5). Native host-network-namespace requirement is documented.

Comment thread src/server.ts
mountCadRender(app)
mountImagesGenerate(app)
mountMetrics(app)
const poolMaintenance = new PoolMaintenance(config.dataDir)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clean up provisioned pools if maintenance initialization fails

If the maintenance journal is malformed, unsafe, or references a legacy directory whose ownership lock cannot be acquired, this constructor throws after buildApp has already provisioned all configured Docker pools. Because buildApp never returns, startServer cannot access or run extras.shutdownHooks; it only releases the instance locks, leaving the newly created, restart-enabled containers running while startup fails. Initialize maintenance before provisioning pools or unwind the accumulated shutdown hooks on this failure path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed forward in #270: initialize and validate the maintenance journal before any configured backend provisions Docker containers. The real startup fixture supplied a malformed journal and valid Docker backend configuration, observed the intended journal refusal, and verified zero containers were created (same focused Beelink2 gate; typecheck passed).

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