Skip to content

Migrate power control handling to roxyd #613

@zmrdltl

Description

@zmrdltl

Background

  • roxyd already routes NodePowerRequest into handlers::power::handle, but src/bin/roxyd/handlers/power.rs is still scaffolding only
  • The legacy roxy path already implements immediate reboot and immediate power-off plus graceful reboot and graceful power-off in src/root/task.rs
  • This issue intentionally covers the full power family rather than only a restart-shaped subset, and the current roxyd control path already routes legacy flat reboot and shutdown requests through node_power
  • In review-protocol, NodePowerResponse::Initiated means the node accepted the power command and may become unreachable shortly after the response is received
  • Because roxyd sends the response only after the power handler returns in the upstream review_protocol::request::handle() flow, an inline Linux reboot(2) or poweroff(2) path cannot satisfy the Initiated reply contract
  • The current dispatch path writes the power response after handlers::power::handle returns, so the immediate reboot and shutdown path requires a small roxyd-local dispatch-side change in addition to the handler work
  • The repository CI currently runs on GitHub-hosted ubuntu-latest and macos-latest, so normal CI coverage must not reboot or shut down the runner
  • This issue is for migration, not behavior redesign

Goal

  • Implement the full power family directly in the roxyd power path
  • Preserve current legacy behavior and platform boundaries while adapting to NodePowerRequest, NodePowerResponse, and the reply timing contract
  • Close the first-pass execution path enough that implementation can start without reopening the design during this issue

Scope

Immediate Reboot And Shutdown

  • Implement NodePowerRequest::Reboot and NodePowerRequest::Shutdown handling in the roxyd power path
  • NodePowerRequest::Shutdown maps to the legacy immediate PowerOff intent
  • Immediate Reboot and Shutdown remain Linux-only as in the legacy path
  • The immediate path must use an implementation that lets roxyd write NodePowerResponse::Initiated successfully before reboot or power-off begins
  • For the first pass, use an in-process pending-operation approach for immediate reboot and power-off
  • The immediate power path must prepare a pending reboot or shutdown operation without executing it immediately
  • The pending operation must wait for an explicit release signal before calling nix::sys::reboot::reboot(...)
  • roxyd must write NodePowerResponse::Initiated on the current request stream before releasing the pending operation
  • roxyd must release the pending operation only after the success response write completes successfully
  • If writing the success response fails, the pending operation must be dropped or cancelled without rebooting or powering off
  • Add a small roxyd-local power-aware dispatch path for immediate NodePowerRequest::Reboot and NodePowerRequest::Shutdown so roxyd owns the response-write ordering
  • Preserve the existing flat reboot and shutdown compatibility requests through the same ordering-safe path
  • This issue does not require a hidden roxyd one-shot mode, hidden internal CLI surface, parent/child readiness protocol, or child-process release protocol
  • Do not call nix::sys::reboot::reboot(...) inline from the normal request handler before the success response is written
  • Do not replace the immediate path with shelling out to reboot or poweroff
  • Unsupported non-Linux immediate requests and preparation failures detected before the success response return Err("invalid command".to_string())

Graceful Reboot And Shutdown

  • Implement NodePowerRequest::GracefulReboot and NodePowerRequest::GracefulShutdown in src/bin/roxyd/handlers/power.rs
  • NodePowerRequest::GracefulShutdown maps to the legacy GracefulPowerOff intent
  • GracefulReboot and GracefulShutdown remain aligned with the current legacy platform behavior
  • Graceful start failures return Err("fail".to_string())

Response And Compatibility

  • Return NodePowerResponse::Initiated when the power request has been accepted and the execution path is ready to proceed
  • Handle power operations directly in roxyd without using run_roxy() or the legacy subprocess path
  • Keep the existing flat reboot and shutdown requests working through the same ordering-safe power path
  • Preserve response-write ordering for immediate reboot and shutdown within roxyd without requiring a review-protocol change
  • Do not introduce a new client-visible failure contract for errors that happen after NodePowerResponse::Initiated has already been returned
  • Keep any additional intentional behavior change out of this issue

Testing

  • If needed for safe testing, allow a minimal helper extraction or other small indirection at the reboot and shutdown operation boundary
  • Add or update tests for power handler/helper behavior and dispatch behavior
  • Normal test and CI runs must not reboot or shut down the real test host or a GitHub Actions runner

Acceptance Criteria

  • Immediate: On Linux, NodePowerRequest::Reboot preserves the current legacy immediate reboot intent and writes NodePowerResponse::Initiated successfully before reboot begins
  • Immediate: On Linux, NodePowerRequest::Shutdown preserves the current legacy immediate power-off intent and writes NodePowerResponse::Initiated successfully before power-off begins
  • Immediate: On non-Linux platforms, immediate Reboot and Shutdown remain unsupported and return the error string invalid command
  • Immediate: reboot and shutdown do not execute the legacy inline nix::sys::reboot::reboot(...) call inside the normal request handler before the success response is written and do not replace the immediate path with shelling out to reboot or poweroff
  • Immediate: preparation failures detected before the success response return invalid command
  • Immediate: the pending operation does not begin reboot or power-off before the current request stream has written NodePowerResponse::Initiated successfully
  • Immediate: if writing the success response fails, the pending operation is dropped or cancelled without rebooting or powering off
  • Graceful: NodePowerRequest::GracefulReboot initiates a graceful reboot and returns NodePowerResponse::Initiated
  • Graceful: NodePowerRequest::GracefulShutdown initiates the legacy graceful power-off behavior and returns NodePowerResponse::Initiated
  • Graceful: start failures return fail rather than unimplemented!() or panic
  • Compatibility: the canonical grouped node_power path and the existing flat reboot and shutdown compatibility paths preserve the expected success and failure behavior
  • Compatibility: response-write ordering for immediate reboot and shutdown is preserved within roxyd without requiring a review-protocol change
  • Compatibility: the roxyd power path does not invoke run_roxy() or the legacy roxy binary
  • Compatibility: this issue does not add a new client-visible failure response for errors that happen after NodePowerResponse::Initiated has already been returned
  • Testing: tests cover direct power handler behavior, grouped node dispatch, and the flat reboot and shutdown compatibility paths without rebooting or shutting down the real test host during normal runs
  • Testing: normal CI test runs do not reboot or shut down a GitHub Actions runner

Tasks

Immediate Reboot And Shutdown

  • Implement NodePowerRequest::Reboot and NodePowerRequest::Shutdown handling in the roxyd power path
  • Map NodePowerRequest::Shutdown to the legacy PowerOff intent
  • Add an in-process pending-operation path for immediate reboot and power-off
  • Add an explicit release step that runs the pending operation only after NodePowerResponse::Initiated is written successfully
  • Add the roxyd-local power-aware dispatch path for immediate NodePowerRequest::Reboot and NodePowerRequest::Shutdown
  • Preserve the flat reboot and shutdown compatibility paths through the same ordering-safe path
  • Implement the immediate reboot and power-off path so roxyd writes NodePowerResponse::Initiated successfully before reboot or power-off begins, without calling nix::sys::reboot::reboot(...) inline from the normal request handler
  • Map unsupported non-Linux immediate requests and preparation failures detected before the success response to Err("invalid command".to_string())

Graceful Reboot And Shutdown

  • Implement NodePowerRequest::GracefulReboot and NodePowerRequest::GracefulShutdown in src/bin/roxyd/handlers/power.rs
  • Map NodePowerRequest::GracefulShutdown to the legacy GracefulPowerOff intent
  • Map GracefulReboot and GracefulShutdown start failures to Err("fail".to_string())

Testing And Compatibility

  • If needed for safe testing, extract a minimal power-operation boundary that allows success and failure paths to be tested without rebooting or shutting down the real host during normal test runs
  • Add focused power handler/helper tests for immediate and graceful power success and failure paths
  • Add a test that verifies a success-response write failure does not allow reboot or power-off to begin
  • Update the roxyd live dispatch tests for the canonical grouped power path and the existing flat reboot and shutdown compatibility requests so they verify the expected success or failure behavior instead of the current unimplemented panic path
  • Verify that the implementation preserves the current platform boundaries and does not depend on the legacy subprocess wrapper path

Non Goals

  • Removing the legacy roxy binary or legacy power code
  • Migrating other handlers under Migrate roxy functionality into roxyd Node handlers #609
  • Behavioral cleanup, API normalization, or client-visible semantic changes around power handling
  • Expanding immediate reboot or shutdown support beyond the current legacy platform boundaries
  • Changing the review-protocol power reply contract or adding a new client-visible failure channel after NodePowerResponse::Initiated
  • Requiring live reboot or shutdown coverage in normal test runs or CI
  • Introducing a hidden roxyd one-shot CLI or subprocess protocol for the first-pass migration

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions