8000 feat(minimald): implement Shutdown RPC by twitchyliquid64 · Pull Request #613 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feat(minimald): implement Shutdown RPC - #613

Merged
twitchyliquid64 merged 1 commit into
mainfrom
tom/stop
Jul 2, 2026
Merged

twitchyliquid64 merged 1 commit into
mainfrom
tom/stop

Conversation

@twitchyliquid64
@twitchyliquid64 twitchyliquid64 commented Jul 1, 2026 •
Copy link
Copy Markdown
Contributor

Tells the minimald to shutdown.

force=false - will error if there are active sessions.
force=true - will kill active sessions/connections, drain them, then exit.

$> echo "{\"force\": true}" | ssh -s  \
    -o ProxyCommand='socat - UNIX-CONNECT:/home/xxx/.local/state/minimal/providers/local-0/ssh.sock' \
    -o 'UserKnownHostsFile=/home/xxx/.local/state/minimal/providers/local-0/known_hosts' \
    local-0 minimald-v1-Shutdown

Summary by CodeRabbit

  • New Features

    • Added a SSH-based one-shot Shutdown RPC with a force option.
    • Server now stops accepting new connections and performs shutdown with a short grace period before terminating remaining session work.
    • Shutdown responses indicate whether termination started or was refused due to active sessions.
  • Bug Fixes

    • New session operations are blocked once shutdown begins.
    • Non-forced shutdown is refused when sessions are live; forced shutdown proceeds and then rejects further session work.
  • Tests

    • Added coverage for the above shutdown scenarios.
  • Chores

    • Added a Unix-domain socket test client helper and updated workspace dependencies.

@coderabbitai
coderabbitai Bot commented Jul 1, 2026 •
Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3d909a4-3602-4742-aa5f-d572d887958e

📥 Commits

Reviewing files that changed from the base of the PR and between 2eba215 and c97d17a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • crates/minimald-rpc/src/lib.rs
  • crates/minimald/Cargo.toml
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/server.rs
  • crates/minimald/src/sessions.rs
  • crates/minimald/src/test_harness.rs
🚧 Files skipped from review as they are similar to previous changes (6)
  • crates/minimald/Cargo.toml
  • crates/minimald/src/test_harness.rs
  • crates/minimald-rpc/src/lib.rs
  • crates/minimald/src/sessions.rs
  • crates/minimald/src/server.rs
  • crates/minimald/src/rpc.rs

📝 Walkthrough

Walkthrough

Adds a new oneshot Shutdown SSH RPC with request/response wire types, server-side shutdown handling with cancellation-based accept-loop teardown and bounded draining, manager-level shutdown guards, and supporting tests plus a UDS test helper.

Changes

Graceful Shutdown Feature

Layer / File(s) Summary
Shutdown RPC contract
crates/minimald-rpc/src/lib.rs
Adds Shutdown, ShutdownRequest with force, tagged ShutdownResponse, and the OneshotSshRpc implementation.
Session manager shutdown state
crates/minimald/src/sessions.rs
Adds in_shutdown, shutdown message handling, session-operation rejection during shutdown, and ManagerHandle::shutdown(force).
Server cancellation and drain
crates/minimald/src/server.rs, crates/minimald/Cargo.toml
Adds shutdown token plumbing, shutdown-aware accept-loop exit, bounded session draining, the tokio-util dependency, and shutdown behavior tests.
RPC dispatch and test support
crates/minimald/src/rpc.rs, crates/minimald/src/test_harness.rs
Adds serve_shutdown, wires it into routing/dispatch, adds shutdown RPC tests, and adds a UDS test client helper.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant serve_shutdown
  participant SessionsManager
  participant ServerState

  Client->>serve_shutdown: ShutdownRequest(force)
  serve_shutdown->>SessionsManager: shutdown(force)
  alt live sessions and not forced
    SessionsManager-->>serve_shutdown: Err(())
    serve_shutdown-->>Client: ShutdownResponse::SessionsLive
  else proceed
    SessionsManager->>SessionsManager: destroy sessions, set in_shutdown
    SessionsManager-->>serve_shutdown: Ok(())
    serve_shutdown->>ServerState: trigger_shutdown()
    ServerState->>ServerState: cancel token, stop accept loop, drain with grace period
    serve_shutdown-->>Client: ShutdownResponse::ShuttingDown
  end
Loading

Possibly related PRs

Suggested reviewers: 0chroma, evanspearman, norrietaylor

Poem

A rabbit hops to bid goodnight,
"Shutdown, please!" — polite, not tight.
Sessions drain in graceful grace,
Then quiet falls upon the place.
No new hops through the door once closed,
The daemon rests, serene, composed. 🐇💤

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding the Shutdown RPC to minimald.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
crates/minimald/src/sessions.rs (1)

225-230: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the repeated in_shutdown guard.

The same if self.in_shutdown { return Err(SessionsError::new(ConnectionRefused, "in shutdown")); } block is duplicated verbatim in GetSession, CreateSession, RenameSession, and DestroySession. A small helper keeps the error text/kind consistent if it ever needs to change.

♻️ Proposed helper
 impl<L: Loader> Manager<L> {
+    fn ensure_not_shutting_down(&self) -> Result<(), SessionsError> {
+        if self.in_shutdown {
+            return Err(SessionsError::new(
+                std::io::ErrorKind::ConnectionRefused,
+                "in shutdown",
+            ));
+        }
+        Ok(())
+    }
+
     fn key_for(&self, pred: &SessionKeyPredicate) -> Result<Option<L::Key>, std::io::Error> {

Then each guard site becomes self.ensure_not_shutting_down()?;.

Also applies to: 284-290, 340-345, 394-399

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minimald/src/sessions.rs` around lines 225 - 230, Extract the repeated
in_shutdown guard in Sessions by adding a small helper on the session manager
(for example, ensure_not_shutting_down) that returns the existing SessionsError
with ConnectionRefused and "in shutdown", then replace the duplicated checks in
GetSession, CreateSession, RenameSession, and DestroySession with calls to that
helper. Keep the helper’s error kind and message identical so all four paths
stay consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/minimald/src/sessions.rs`:
- Around line 432-449: Forced shutdown in ManagerMessage::Shutdown currently
destroys live sessions without first deregistering HostNet hostnames, unlike
DestroySession. Update the shutdown teardown path in sessions.rs to mirror the
deregistration step used by DestroySession: before calling hnd.destroy() for
each live session, remove the associated hostname entries from self.hostnames so
HostNet sessions do not leave stale mappings during the shutdown drain window.
Use the existing shutdown loop in ManagerMessage::Shutdown and the
session/hostname tracking logic around self.running and self.hostnames to place
the deregistration in the same teardown flow.
- Around line 432-449: ManagerMessage::Shutdown currently waits on each
SessionHandle::destroy() sequentially with no timeout, so one stuck session can
block shutdown indefinitely. Update the shutdown path in
ManagerMessage::Shutdown to use a bounded timeout or concurrent teardown for all
entries in self.running, and make sure self.in_shutdown is still set before
teardown begins. If you add helper logic, keep it anchored around
SessionHandle::destroy() and the ManagerMessage::Shutdown match arm so the
shutdown behavior remains controlled and cannot hang forever.

---

Nitpick comments:
In `@crates/minimald/src/sessions.rs`:
- Around line 225-230: Extract the repeated in_shutdown guard in Sessions by
adding a small helper on the session manager (for example,
ensure_not_shutting_down) that returns the existing SessionsError with
ConnectionRefused and "in shutdown", then replace the duplicated checks in
GetSession, CreateSession, RenameSession, and DestroySession with calls to that
helper. Keep the helper’s error kind and message identical so all four paths
stay consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 76538df8-cf3b-4147-8255-1b61352a166e

📥 Commits

Reviewing files that changed from the base of the PR and between c4d6f82 and 2eba215.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • crates/minimald-rpc/src/lib.rs
  • crates/minimald/Cargo.toml
  • crates/minimald/src/rpc.rs
  • crates/minimald/src/server.rs
  • crates/minimald/src/sessions.rs
  • crates/minimald/src/test_harness.rs

Comment thread crates/minimald/src/sessions.rs Resolved
@0chroma 0chroma left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM! well thought out ^^

@twitchyliquid64
twitchyliquid64 merged commit fb618f9 into main Jul 2, 2026
41 checks passed
@twitchyliquid64
twitchyliquid64 deleted the tom/stop branch July 2, 2026 16:51
0chroma added a commit that referenced this pull request Jul 6, 2026
Adds a `minimal stop` command that sends the Shutdown RPC (PR #613)
to the running minimald daemon. With no flags it requests a graceful
shutdown — the daemon refuses if any session is live. Pass --force to
tear down live sessions and shut down regardless.

Includes three integration tests covering the no-sessions, refused,
and forced paths. Also adds a bring_session_up helper to the test
harness so tests can make sessions "live" for shutdown scenarios.
0chroma added a commit that referenced this pull request Jul 8, 2026
Adds a `minimal stop` command that sends the Shutdown RPC (PR #613)
to the running minimald daemon. With no flags it requests a graceful
shutdown — the daemon refuses if any session is live. Pass --force to
tear down live sessions and shut down regardless.

Includes three integration tests covering the no-sessions, refused,
and forced paths. Also adds a bring_session_up helper to the test
harness so tests can make sessions "live" for shutdown scenarios.
0chroma added a commit that referenced this pull request Jul 8, 2026
…nds, test harness, stop (#612)

* feat(minimal): add CLI subcommands, test harness, and lib/main split

Rebase the minimal2 CLI work onto main, adapting to the crate rename
(minimal2 → minimal, minimal → mip). Combines the following features:

- version, rename, init, add, and update subcommands
- --json and --raw output flags for the ls command
- anyhow-based error handling replacing ad-hoc eprintln!+Err(())
- resolve_session helper for UUID/name session lookups
- integration test harness with 14 CLI tests exercising real daemon
- lib/main split so integration tests can call cmd_* functions directly
- minimald test-support feature exposing TestServer/TestClient externally
- merged main's session-creation flow (SessionConfig, Pending/Ready,
  AbortOnUnapproved, drive_pending_to_active, send_abort) into the
  anyhow-based error handling
- client.rs socket path now uses paths::minimal_state_dir()

* feat(minimal): add stop subcommand to shut down minimald

Adds a `minimal stop` command that sends the Shutdown RPC (PR #613)
to the running minimald daemon. With no flags it requests a graceful
shutdown — the daemon refuses if any session is live. Pass --force to
tear down live sessions and shut down regardless.

Includes three integration tests covering the no-sessions, refused,
and forced paths. Also adds a bring_session_up helper to the test
harness so tests can make sessions "live" for shutdown scenarios.

* refactor(minimal): apply code review fixes to lib/main split

Reduce duplication and improve test coverage after the lib/main
split: extract SessionLookup/ensure_daemon helpers, make
parse_ingress return anyhow::Error, privatize shell_quote, and
capture stdout in ls integration tests via a new format_ls function.

* fix(minimal): gate build-system commands to Linux for macOS compatibility

The init/add/update subcommands depend on mctx → hakoniwa → libcgroups
→ procfs, which only compiles on Linux. Gating mctx/op behind
target_os = "linux" keeps the minimal binary buildable on macOS, fixing
the autospawn-e2e CI lane that builds and runs minimal on the self-hosted
Apple Silicon runner.
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.

2 participants

0