feat: add control board replacement workflow

This commit is contained in:
lussierj committed 2026-08-02 14:34:16 +00:00
1 parent 62c91a2c3e
commit bcf1b4e3cf
16 files changed
+2122 -279

No files matched your search

+64 -4
View File
@@ -62,8 +62,52 @@ fi
echo "Validating Compose configuration..."
MINEROPS_DEV_PORT="${dev_port}" "${compose[@]}" config --quiet
echo "Building and starting disposable development stack..."
MINEROPS_DEV_PORT="${dev_port}" "${compose[@]}" up --detach --build
echo "Starting disposable data services..."
MINEROPS_DEV_PORT="${dev_port}" "${compose[@]}" up --detach postgres redis
echo "Waiting for PostgreSQL initialization and Redis..."
for attempt in $(seq 1 180); do
postgres_ready=false
redis_ready=false
if "${compose[@]}" exec -T postgres sh -ec \
'test "$(cat /proc/1/comm)" = "postgres"; psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atqc "SELECT 1"' \
2>/dev/null | grep -qx '1'; then
postgres_ready=true
fi
if "${compose[@]}" exec -T redis redis-cli ping 2>/dev/null | grep -qx 'PONG'; then
redis_ready=true
fi
if [[ "${postgres_ready}" == "true" && "${redis_ready}" == "true" ]]; then
break
fi
if (( attempt == 180 )); then
echo "Disposable data services did not become ready within 180 seconds." >&2
exit 1
fi
sleep 1
done
echo "Building and starting the disposable backend..."
MINEROPS_DEV_PORT="${dev_port}" "${compose[@]}" up --detach --build --no-deps backend
backend_container="$("${compose[@]}" ps -q backend)"
for attempt in $(seq 1 180); do
backend_health="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "${backend_container}")"
if [[ "${backend_health}" == "healthy" ]]; then
break
fi
if [[ "${backend_health}" == "exited" || "${backend_health}" == "dead" ]]; then
echo "Disposable backend exited before becoming healthy." >&2
exit 1
fi
if (( attempt == 180 )); then
echo "Disposable backend did not become healthy within 180 seconds." >&2
exit 1
fi
sleep 1
done
echo "Starting the disposable gateway..."
MINEROPS_DEV_PORT="${dev_port}" "${compose[@]}" up --detach --no-deps gateway
echo "Waiting for the gateway health endpoint..."
health_file="${tmp_prefix}-health.json"
@@ -88,7 +132,7 @@ health_json="$(cat "${health_file}")"
echo "Checking migrations and required schema..."
current_revision="$("${compose[@]}" exec -T backend alembic current 2>/dev/null)"
[[ "${current_revision}" == *"0012_return_to_service"* ]]
[[ "${current_revision}" == *"0013_control_board_replacement"* ]]
missing_tables="$(
"${compose[@]}" exec -T postgres sh -c \
@@ -168,6 +212,8 @@ echo "Checking Begin Repair permission and workflow..."
INSERT INTO miners (
id,
asset_tag,
current_mac,
control_board_sn,
status,
lifecycle_status,
first_seen_at,
@@ -176,6 +222,8 @@ INSERT INTO miners (
VALUES (
'00000000-0000-0000-0000-000000000981',
'SMOKE-REPAIR-001',
'02:AA:BB:CC:EE:01',
'SMOKE-CB-OLD',
'unknown',
'active',
NULL,
@@ -202,19 +250,31 @@ smoke_repair_id="$(
| python3 -c 'import json, sys; print(json.load(sys.stdin)["data"]["repair_id"])'
)"
[[ -n "${smoke_repair_id}" ]]
[[ "$(
status_for "${readonly_cookie}" POST /api/miners/00000000-0000-0000-0000-000000000981/replace-control-board \
'{"new_mac_address":"02:AA:BB:CC:EE:02","new_control_board_serial":"SMOKE-CB-NEW","removed_board_disposition":"sent_for_repair","replacement_reason":"failed"}'
)" == "403" ]]
board_response="$(curl --fail --silent -b "${technician_cookie}" \
-H 'Content-Type: application/json' \
--data '{"new_mac_address":"02:AA:BB:CC:EE:02","new_control_board_serial":"SMOKE-CB-NEW","removed_board_disposition":"sent_for_repair","replacement_reason":"failed","notes":"Smoke replacement."}' \
"${base_url}/api/miners/00000000-0000-0000-0000-000000000981/replace-control-board")"
[[ "${board_response}" == *'"current_mac":"02:AA:BB:CC:EE:02"'* ]]
[[ "${board_response}" == *'"event_sequence":3'* ]]
readonly_repair_detail="$(
curl --fail --silent -b "${readonly_cookie}" \
"${base_url}/api/repairs/${smoke_repair_id}"
)"
[[ "${readonly_repair_detail}" == *'"miner_asset_id":"SMOKE-REPAIR-001"'* ]]
[[ "${readonly_repair_detail}" == *'"miner_lifecycle":"in_repair"'* ]]
[[ "${readonly_repair_detail}" == *'"latest_event_sequence":2'* ]]
[[ "${readonly_repair_detail}" == *'"latest_event_sequence":3'* ]]
readonly_repair_events="$(
curl --fail --silent -b "${readonly_cookie}" \
"${base_url}/api/repairs/${smoke_repair_id}/events"
)"
[[ "${readonly_repair_events}" == *'"sequence":1'* ]]
[[ "${readonly_repair_events}" == *'"sequence":2'* ]]
[[ "${readonly_repair_events}" == *'"sequence":3'* ]]
[[ "${readonly_repair_events}" == *'"event_type":"CONTROL_BOARD_REPLACED"'* ]]
[[ "${readonly_repair_events}" == *'"event_type":"REPAIR_STARTED"'* ]]
[[ "${readonly_repair_events}" == *'"event_type":"INITIAL_NOTE"'* ]]
[[ "$(status_for "${technician_cookie}" GET /api/repairs/queue)" == "200" ]]