refactor: extract validate_startable from start.rs handle#135
refactor: extract validate_startable from start.rs handle#135mukeshblackhat wants to merge 4 commits intocoast-guard:mainfrom
Conversation
Extract status validation into a pure function with an exhaustive match on all InstanceStatus variants, following the same pattern as validate_stoppable in stop.rs (PR coast-guard#126). Startable: Stopped, Idle, Enqueued, Unassigning (preserving original fall-through behavior). Rejected: Running/CheckedOut (already running), Provisioning/Assigning/Starting/Stopping (transitional). Adds 10 unit tests covering every InstanceStatus variant. The #[allow] annotations on handle remain as it still triggers both lints. Closes coast-guard#133
coast-daemon/src/handlers/start.rs
Outdated
There was a problem hiding this comment.
but we still need to remove this to confirm if it works
There was a problem hiding this comment.
even if we separate them as well, we will not be able to remove the complete
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
still the lines will be over the limit and complexity
these will be the hardest one to remove as they are asycn and
Load image, Compose up and Wait for health checks
they are interconnected and depended as well so trickiest part here.
There was a problem hiding this comment.
Yeah, let's try that. Make sure to add new tests for new code introduced. I'll give it a test after.
There was a problem hiding this comment.
Phase 2 & 3: Extract remaining phases from handle, remove #[allow]
Three commits pushed that complete the extraction work.
What changed
Extracted 8 helpers from handle:
verify_inner_daemon_health— Runsdocker infowith 10s timeout, normalizes socket permissionsbuild_workspace_mount_command— Pure string construction for mount commandstart_bare_services_if_present— Checks supervisor dir, starts bare servicesreapply_workspace_mount— Computes mount source, builds symlink fix, execs mountsetup_shared_services— Builds routing targets, plans + ensures proxiesrun_compose_and_wait_for_health— Compose up + polls health until readybackfill_build_id— Reads latest symlink, persists build ID in DBrun_docker_operations— Orchestrates all Docker phase steps
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)] removed — both lints now pass without suppression.
22 new tests added (46 total in the module, up from 14).
Why files other than start.rs were touched
coast-daemon/Cargo.toml
Added async-trait to [dev-dependencies]. Needed because MockRuntime in our tests implements the Runtime trait which uses #[async_trait]. This is a dev-only dependency — no runtime impact.
coast-daemon/src/handlers/run/service_start.rs
Changed compose_ps_output_is_ready from fn to pub(crate) fn. The inline compose health check in start.rs was duplicating the exact same logic. Instead of copying it, we reuse the existing tested
function. DRY fix.
coast-daemon/src/handlers/run/mod.rs
Added pub(crate) use service_start::compose_ps_output_is_ready; re-export so start.rs can access it.
Cargo.lock
Auto-updated from the async-trait addition.
Why MockRuntime and &dyn Runtime
The extracted helpers need Docker operations (exec_in_coast). To test them without a real Docker daemon, we use &dyn Runtime (the trait from coast-docker/src/runtime.rs) instead of &bollard::Docker. This
lets us pass a MockRuntime in tests that returns scripted ExecResult values.
This is the same pattern already used in coast-docker/src/container.rs — ContainerManager<R: Runtime> with a MockRuntime in its test module.
We also changed normalize_inner_docker_socket_permissions from &bollard::Docker to &dyn Runtime for the same reason. Note: provision.rs has its own separate private copy of that function — it was not
touched.
No public API changes
pub async fn handlesignature unchanged — same 3 params, same return type- All 3 callers (
mod.rs:550,mod.rs:570,rerun_extractors.rs:327) unaffected - All new functions are private to
start.rs
…pose polling from start.rs handle Phase 2 of start.rs handle extraction (issue coast-guard#133): - Extract verify_inner_daemon_health: runs docker info with 10s timeout, normalizes socket permissions on success, returns typed errors on failure. - Extract build_workspace_mount_command: pure string construction for the /workspace bind mount shell command. - Extract start_bare_services_if_present: checks supervisor dir existence and starts bare services via Runtime trait. - DRY fix: replace inline compose health check with existing compose_ps_output_is_ready from service_start.rs (made pub(crate)). - Change normalize_inner_docker_socket_permissions to take &dyn Runtime instead of &bollard::Docker for testability. - Add MockRuntime and 8 new tests covering all extracted helpers. - Add async-trait to dev-dependencies for mock implementation.
…ow] suppression Phase 3 of start.rs handle extraction (issue coast-guard#133): - Extract reapply_workspace_mount: computes mount source, builds symlink fix, executes mount command, logs warnings on failure. - Extract setup_shared_services: builds routing targets, plans routing, ensures proxies. Returns Result for caller to handle revert. - Extract run_compose_and_wait_for_health: runs compose up and polls compose ps using compose_ps_output_is_ready until services are ready. - Extract backfill_build_id: reads latest symlink to discover build ID and persists it in the database. - Extract run_docker_operations: orchestrates all Docker phase steps, flattening the nested if-let structure that drove cognitive complexity. - Remove #[allow(clippy::cognitive_complexity, clippy::too_many_lines)] from handle — both lints now pass without suppression. - Add 4 new tests for reapply_workspace_mount and run_compose_and_wait_for_health.
Summary
validate_startable— pure function with an exhaustivematchon allInstanceStatusvariantshandleto call the helper instead of inlineifchainsInstanceStatusvariant#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]kept onhandle— still triggers both lints after extraction (follow-up ticket)What was there before
handle(line 48) had status validation inlined asifchains at lines 64-79. Two conditions: one rejectingRunning/CheckedOut("already running"), another rejectingProvisioning/Assigning/Starting/Stopping("currently..."). Everything else fell through as startable.What changed
Single file:
coast-daemon/src/handlers/start.rsExtracted function:
Stopped,Idle,Enqueued,UnassigningOk(())Running,CheckedOutProvisioning,Assigning,Starting,StoppingFollows the same pattern as
validate_stoppableinstop.rs(PR #126).Behavior preservation
The original code only explicitly rejected
Running,CheckedOut, and the 4 transitional states.Stopped,Idle,Enqueued, andUnassigningall fell through as startable. Our exhaustivematchpreserves this exactly — no new rejections added.All 3 callers of
start::handle(mod.rs:550,mod.rs:570,rerun_extractors.rs:327) handleOk/Errgenerically and are unaffected.Test plan
Run new tests
Run lint and full tests
Verify no regressions in related handlers
Closes #133