Debian installs show the Controller drive wizard

wrolpi-bootstrap.sh created controller.yaml on installed systems, which
hid the onboarding wizard.  Installed systems now wait for the wizard,
whose repair.sh finishes setup.

"Run repair without external drive" now writes controller.yaml before
starting repair, so the wizard does not return after repair.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Roland 2026-10-13 15:42:55 -06:00
parent 697a8180c7
commit f3232f30ac
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
6 changed files with 119 additions and 1 deletions

View file

@ -14,6 +14,7 @@ from controller.lib.onboarding import (
cancel_probe,
commit_onboarding,
get_onboarding_candidates,
onboard_without_drive,
probe_drive,
)
@ -516,3 +517,37 @@ class TestCommitOnboardingShadowedData:
result = commit_onboarding("/dev/sda1", "ext4", force_shadowed=True)
assert result["success"] is True
class TestOnboardWithoutDrive:
"""Tests for onboard_without_drive."""
def test_writes_marker_and_starts_repair(self, mock_docker_mode, tmp_path):
"""The library stays on the internal disk, and the wizard does not return after repair."""
media_dir = tmp_path / "wrolpi"
media_dir.mkdir()
config_path = media_dir / "config" / "controller.yaml"
with mock.patch("controller.lib.onboarding.get_media_directory", return_value=media_dir), \
mock.patch("controller.lib.config.CONFIG_PATH_ON_DRIVE", config_path), \
mock.patch("controller.lib.onboarding.start_script", return_value={"success": True}) as start:
result = onboard_without_drive()
assert result == {"success": True, "mounts": [], "repair_started": True}
assert config_path.is_file()
start.assert_called_once_with("repair")
def test_repair_failure_is_reported(self, mock_docker_mode, tmp_path):
media_dir = tmp_path / "wrolpi"
media_dir.mkdir()
config_path = media_dir / "config" / "controller.yaml"
with mock.patch("controller.lib.onboarding.get_media_directory", return_value=media_dir), \
mock.patch("controller.lib.config.CONFIG_PATH_ON_DRIVE", config_path), \
mock.patch("controller.lib.onboarding.start_script",
return_value={"success": False, "error": "already running"}):
result = onboard_without_drive()
assert result["success"] is True
assert result["repair_started"] is False
assert config_path.is_file()

View file

@ -15,6 +15,7 @@ class TestOnboardingDockerAndMounted:
("post", "/api/onboarding/probe", {"device_path": "/dev/sda1", "fstype": "ext4"}),
("post", "/api/onboarding/commit", {"device_path": "/dev/sda1", "fstype": "ext4"}),
("post", "/api/onboarding/cancel", None),
("post", "/api/onboarding/without-drive", None),
])
def test_endpoint_returns_501_in_docker(self, test_client_docker_mode, method, endpoint, payload):
client_call = getattr(test_client_docker_mode, method)
@ -24,6 +25,7 @@ class TestOnboardingDockerAndMounted:
@pytest.mark.parametrize("method,endpoint,payload", [
("get", "/api/onboarding/candidates", None),
("post", "/api/onboarding/probe", {"device_path": "/dev/sda1", "fstype": "ext4"}),
("post", "/api/onboarding/without-drive", None),
])
def test_endpoint_returns_409_when_drive_mounted(self, test_client, method, endpoint, payload):
"""Onboarding endpoints reject when the primary drive is already mounted."""
@ -196,3 +198,26 @@ class TestOnboardingCancelEndpoint:
):
response = test_client.post("/api/onboarding/cancel")
assert response.status_code == 200
class TestOnboardingWithoutDrive:
def test_without_drive_success(self, test_client):
with mock.patch("controller.api.onboarding.is_primary_drive_mounted", return_value=False), \
mock.patch("controller.api.onboarding.onboard_without_drive",
return_value={"success": True, "mounts": [], "repair_started": True}) as onboard:
response = test_client.post("/api/onboarding/without-drive")
assert response.status_code == 200
assert response.json()["success"] is True
assert response.json()["repair_started"] is True
onboard.assert_called_once_with()
def test_without_drive_failure(self, test_client):
with mock.patch("controller.api.onboarding.is_primary_drive_mounted", return_value=False), \
mock.patch("controller.api.onboarding.onboard_without_drive",
return_value={"success": False, "error": "disk full"}):
response = test_client.post("/api/onboarding/without-drive")
assert response.status_code == 500
assert "disk full" in response.json()["detail"]