feat: add agent zones
This commit is contained in:
1 parent
d8cd7ad058
commit
82309db79a
29 files changed
+2242
-255
No files matched your search
@@ -78,8 +78,8 @@ migration_head="$(
|
||||
"${compose[@]}" exec -T postgres sh -ec \
|
||||
'psql -v ON_ERROR_STOP=1 -At -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT version_num FROM alembic_version"'
|
||||
)"
|
||||
if [[ "${migration_head}" != "0015_miner_control_foundation" ]]; then
|
||||
echo "Refusing demo seed: database Alembic head is ${migration_head:-missing}, expected 0015_miner_control_foundation." >&2
|
||||
if [[ "${migration_head}" != "0016_agent_zones" ]]; then
|
||||
echo "Refusing demo seed: database Alembic head is ${migration_head:-missing}, expected 0016_agent_zones." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
project_name="${MINEROPS_AGENT_ZONES_MIGRATION_PROJECT:-minerops-agent-zones-migration-$$}"
|
||||
compose=(docker compose --project-name "${project_name}" --env-file .env.dev -f compose.dev.yaml)
|
||||
|
||||
cleanup() {
|
||||
status=$?
|
||||
if (( status != 0 )); then
|
||||
"${compose[@]}" logs --no-color --tail 160 postgres || true
|
||||
fi
|
||||
"${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true
|
||||
exit "${status}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
run_alembic() { "${compose[@]}" run --rm --no-deps backend alembic "$@"; }
|
||||
psql_db() {
|
||||
"${compose[@]}" exec -T postgres sh -ec \
|
||||
'exec psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" "$@"' sh "$@"
|
||||
}
|
||||
|
||||
[[ -f .env.dev ]] || { echo "Missing .env.dev" >&2; exit 1; }
|
||||
"${compose[@]}" up --detach postgres
|
||||
for attempt in $(seq 1 60); do
|
||||
"${compose[@]}" exec -T postgres sh -ec \
|
||||
'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"' >/dev/null 2>&1 && break
|
||||
(( attempt < 60 )) || { echo "PostgreSQL did not become ready" >&2; exit 1; }
|
||||
sleep 1
|
||||
done
|
||||
"${compose[@]}" build backend
|
||||
|
||||
site_a="00000000-0000-4000-8000-000000001690"
|
||||
site_b="00000000-0000-4000-8000-000000001691"
|
||||
miner_a="00000000-0000-4000-8000-000000001692"
|
||||
|
||||
echo "Testing production path 0014 -> 0015 -> 0016 and truthful unassigned data..."
|
||||
run_alembic upgrade 0014_repair_parts_tracking
|
||||
psql_db -c "INSERT INTO minerops_site_agents (agent_id, site_name) VALUES ('legacy-zone-unassigned', 'Legacy Display')"
|
||||
run_alembic upgrade 0015_miner_control_foundation
|
||||
psql_db <<SQL
|
||||
INSERT INTO sites (id, name) VALUES
|
||||
('${site_a}', 'Agent Zone Site A'),
|
||||
('${site_b}', 'Agent Zone Site B');
|
||||
INSERT INTO miners (id, site_id, asset_tag, first_seen_at, last_seen_at)
|
||||
VALUES ('${miner_a}', '${site_a}', 'AGENT-ZONE-MIGRATION', NULL, NULL);
|
||||
INSERT INTO minerops_site_agents (agent_id, site_id, site_name)
|
||||
VALUES ('zone-agent-a', '${site_a}', 'Agent Zone Site A');
|
||||
INSERT INTO minerops_agent_snapshots (agent_id, site_name, miner_count, payload)
|
||||
VALUES ('zone-agent-a', 'Agent Zone Site A', 0, '{}');
|
||||
INSERT INTO minerops_agent_discoveries (
|
||||
id, agent_id, site_name, ip_address, raw_payload, discovery_status
|
||||
) VALUES (
|
||||
'agent-zone-discovery', 'zone-agent-a', 'Agent Zone Site A',
|
||||
'192.0.2.90', '{}', 'new'
|
||||
);
|
||||
SQL
|
||||
run_alembic upgrade 0016_agent_zones
|
||||
[[ "$(run_alembic current 2>/dev/null)" == *"0016_agent_zones"* ]]
|
||||
|
||||
psql_db <<SQL
|
||||
DO \$\$
|
||||
DECLARE zone_a uuid;
|
||||
BEGIN
|
||||
SELECT id INTO zone_a FROM agent_zones
|
||||
WHERE site_id = '${site_a}' AND name = 'Default Zone';
|
||||
IF zone_a IS NULL THEN
|
||||
RAISE EXCEPTION 'Default Zone was not created for Site A';
|
||||
END IF;
|
||||
IF (SELECT agent_zone_id FROM miners WHERE id = '${miner_a}') IS DISTINCT FROM zone_a THEN
|
||||
RAISE EXCEPTION 'Miner was not backfilled to its Site default Zone';
|
||||
END IF;
|
||||
IF (SELECT agent_zone_id FROM minerops_site_agents WHERE agent_id = 'zone-agent-a')
|
||||
IS DISTINCT FROM zone_a THEN
|
||||
RAISE EXCEPTION 'Agent was not backfilled to its Site default Zone';
|
||||
END IF;
|
||||
IF (SELECT agent_zone_id FROM minerops_agent_snapshots WHERE agent_id = 'zone-agent-a' LIMIT 1)
|
||||
IS DISTINCT FROM zone_a THEN
|
||||
RAISE EXCEPTION 'Snapshot Zone backfill failed';
|
||||
END IF;
|
||||
IF (SELECT agent_zone_id FROM minerops_agent_discoveries WHERE id = 'agent-zone-discovery')
|
||||
IS DISTINCT FROM zone_a THEN
|
||||
RAISE EXCEPTION 'Discovery Zone backfill failed';
|
||||
END IF;
|
||||
IF (SELECT agent_zone_id FROM minerops_site_agents
|
||||
WHERE agent_id = 'legacy-zone-unassigned') IS NOT NULL THEN
|
||||
RAISE EXCEPTION 'Legacy Agent without a durable Site was guessed into a Zone';
|
||||
END IF;
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_indexes
|
||||
WHERE indexname = 'uq_minerops_site_agents_agent_zone_id') THEN
|
||||
RAISE EXCEPTION 'One-Agent-per-Zone index missing';
|
||||
END IF;
|
||||
IF EXISTS (SELECT 1 FROM pg_indexes
|
||||
WHERE indexname = 'uq_minerops_site_agents_site_id') THEN
|
||||
RAISE EXCEPTION 'One-Agent-per-Site index was not removed';
|
||||
END IF;
|
||||
END \$\$;
|
||||
SQL
|
||||
|
||||
echo "Testing CIDR, active-name, cross-Site, and one-Agent-per-Zone integrity..."
|
||||
psql_db <<SQL
|
||||
INSERT INTO agent_zone_network_ranges (agent_zone_id, network)
|
||||
SELECT id, '10.30.0.0/24'::cidr FROM agent_zones
|
||||
WHERE site_id = '${site_a}' AND name = 'Default Zone';
|
||||
DO \$\$
|
||||
DECLARE zone_a uuid;
|
||||
BEGIN
|
||||
SELECT id INTO zone_a FROM agent_zones
|
||||
WHERE site_id = '${site_a}' AND name = 'Default Zone';
|
||||
IF (SELECT network::text FROM agent_zone_network_ranges
|
||||
WHERE agent_zone_id = zone_a) <> '10.30.0.0/24' THEN
|
||||
RAISE EXCEPTION 'CIDR was not stored canonically';
|
||||
END IF;
|
||||
BEGIN
|
||||
INSERT INTO agent_zones (site_id, name) VALUES ('${site_a}', 'default zone');
|
||||
RAISE EXCEPTION 'Case-insensitive active Zone duplicate was accepted';
|
||||
EXCEPTION WHEN unique_violation THEN NULL;
|
||||
END;
|
||||
BEGIN
|
||||
INSERT INTO minerops_site_agents (agent_id, site_id, agent_zone_id, site_name)
|
||||
VALUES ('duplicate-zone-agent', '${site_a}', zone_a, 'Duplicate');
|
||||
RAISE EXCEPTION 'Second Agent in one Zone was accepted';
|
||||
EXCEPTION WHEN unique_violation THEN NULL;
|
||||
END;
|
||||
BEGIN
|
||||
UPDATE miners SET site_id = '${site_b}' WHERE id = '${miner_a}';
|
||||
RAISE EXCEPTION 'Cross-Site miner Zone combination was accepted';
|
||||
EXCEPTION WHEN foreign_key_violation THEN NULL;
|
||||
END;
|
||||
END \$\$;
|
||||
SQL
|
||||
|
||||
echo "Confirming CIDR values with host bits are rejected..."
|
||||
if psql_db -c "INSERT INTO agent_zone_network_ranges (agent_zone_id, network) SELECT id, '10.30.0.7/24'::cidr FROM agent_zones WHERE site_id = '${site_a}' AND name = 'Default Zone'"; then
|
||||
echo "Host-address CIDR unexpectedly passed PostgreSQL validation" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
echo "Testing downgrade refusal for irreducible network configuration..."
|
||||
if output="$(run_alembic downgrade 0015_miner_control_foundation 2>&1)"; then
|
||||
echo "Downgrade unexpectedly accepted Agent Zone network ranges" >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ "${output}" == *"cannot be represented by the one-Agent-per-Site model"* ]]
|
||||
[[ "$(run_alembic current 2>/dev/null)" == *"0016_agent_zones"* ]]
|
||||
[[ "$(psql_db -Atc "SELECT count(*) FROM agent_zone_network_ranges")" == "1" ]]
|
||||
psql_db -c "DELETE FROM agent_zone_network_ranges"
|
||||
|
||||
echo "Testing downgrade refusal with multiple Agents at one Site..."
|
||||
psql_db <<SQL
|
||||
WITH created AS (
|
||||
INSERT INTO agent_zones (site_id, name)
|
||||
VALUES ('${site_a}', 'Room 2') RETURNING id
|
||||
)
|
||||
INSERT INTO minerops_site_agents (agent_id, site_id, agent_zone_id, site_name)
|
||||
SELECT 'zone-agent-b', '${site_a}', id, 'Agent Zone Site A' FROM created;
|
||||
SQL
|
||||
if output="$(run_alembic downgrade 0015_miner_control_foundation 2>&1)"; then
|
||||
echo "Downgrade unexpectedly accepted multiple Agents at one Site" >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ "${output}" == *"multiple Agent installations are assigned"* ]]
|
||||
[[ "${output}" == *"${site_a}"* ]]
|
||||
[[ "$(run_alembic current 2>/dev/null)" == *"0016_agent_zones"* ]]
|
||||
[[ "$(psql_db -Atc "SELECT count(*) FROM minerops_site_agents WHERE site_id='${site_a}'")" == "2" ]]
|
||||
psql_db -c "DELETE FROM minerops_site_agents WHERE agent_id = 'zone-agent-b'; DELETE FROM agent_zones WHERE site_id = '${site_a}' AND name = 'Room 2'"
|
||||
|
||||
echo "Testing legal downgrade, data preservation, and re-upgrade..."
|
||||
run_alembic downgrade 0015_miner_control_foundation
|
||||
[[ "$(run_alembic current 2>/dev/null)" == *"0015_miner_control_foundation"* ]]
|
||||
[[ "$(psql_db -Atc "SELECT count(*) FROM miners WHERE id='${miner_a}'")" == "1" ]]
|
||||
[[ "$(psql_db -Atc "SELECT count(*) FROM minerops_site_agents WHERE agent_id='zone-agent-a'")" == "1" ]]
|
||||
[[ "$(psql_db -Atc "SELECT count(*) FROM information_schema.tables WHERE table_name='agent_zones'")" == "0" ]]
|
||||
run_alembic upgrade 0016_agent_zones
|
||||
[[ "$(run_alembic current 2>/dev/null)" == *"0016_agent_zones"* ]]
|
||||
|
||||
echo "Testing empty database through head..."
|
||||
"${compose[@]}" exec -T postgres sh -ec 'createdb -U "$POSTGRES_USER" minerops_agent_zones_empty'
|
||||
"${compose[@]}" run --rm --no-deps -e POSTGRES_DB=minerops_agent_zones_empty backend alembic upgrade head
|
||||
empty_revision="$("${compose[@]}" exec -T postgres sh -ec \
|
||||
'psql -U "$POSTGRES_USER" -d minerops_agent_zones_empty -Atc "SELECT version_num FROM alembic_version"')"
|
||||
[[ "${empty_revision}" == "0016_agent_zones" ]]
|
||||
|
||||
echo "Agent Zones migration lifecycle checks passed."
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "${repo_root}"
|
||||
project_name="${MINEROPS_AGENT_ZONES_TEST_PROJECT:-minerops-agent-zones-test-$$}"
|
||||
compose=(docker compose --project-name "${project_name}" --env-file .env.dev -f compose.dev.yaml)
|
||||
|
||||
cleanup() {
|
||||
status=$?
|
||||
if (( status != 0 )); then
|
||||
"${compose[@]}" logs --no-color --tail 160 postgres || true
|
||||
fi
|
||||
"${compose[@]}" down --volumes --remove-orphans >/dev/null 2>&1 || true
|
||||
exit "${status}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
[[ -f .env.dev ]] || { echo "Missing .env.dev" >&2; exit 1; }
|
||||
for source_file in \
|
||||
backend/tests/test_agent_zones.py \
|
||||
backend/tests/test_agent_zones_postgres.py \
|
||||
backend/tests/test_miner_control.py \
|
||||
backend/tests/test_miner_control_postgres.py \
|
||||
backend/tests/test_agent_commands.py; do
|
||||
permissions="$(stat -c '%a' "${source_file}")"
|
||||
(( (10#${permissions} % 10) & 4 )) || {
|
||||
echo "${source_file} is not readable by the non-root test container" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
"${compose[@]}" up --detach postgres
|
||||
for attempt in $(seq 1 60); do
|
||||
"${compose[@]}" exec -T postgres sh -ec \
|
||||
'pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"' >/dev/null 2>&1 && break
|
||||
(( attempt < 60 )) || { echo "PostgreSQL did not become ready" >&2; exit 1; }
|
||||
sleep 1
|
||||
done
|
||||
"${compose[@]}" build backend
|
||||
"${compose[@]}" run --rm --no-deps backend alembic upgrade head
|
||||
"${compose[@]}" run --rm --no-deps \
|
||||
-v "${repo_root}/backend/tests:/app/tests:ro" \
|
||||
-v "${repo_root}/agents:/agents:ro" \
|
||||
backend python -m unittest -v \
|
||||
tests.test_agent_zones \
|
||||
tests.test_agent_zones_postgres \
|
||||
tests.test_miner_control \
|
||||
tests.test_miner_control_postgres \
|
||||
tests.test_agent_commands
|
||||
|
||||
scripts/test-windows-agent-source.sh
|
||||
echo "Agent Zones focused tests passed."
|
||||
@@ -132,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}" == *"0015_miner_control_foundation"* ]]
|
||||
[[ "${current_revision}" == *"0016_agent_zones"* ]]
|
||||
|
||||
missing_tables="$(
|
||||
"${compose[@]}" exec -T postgres sh -c \
|
||||
|
||||
@@ -5,6 +5,10 @@ service="agents/windows/MinerOpsAgentService.ps1"
|
||||
control="agents/windows/MinerOpsControl.ps1"
|
||||
installer="agents/windows/install.ps1"
|
||||
rg -q 'X-MinerOps-Agent-ID' "${service}"
|
||||
rg -Fq '$config.agent_zone_id' "${service}"
|
||||
rg -Fq '$body["agent_zone_id"]' "${service}"
|
||||
rg -q '"agent_zone_id"' agents/windows/config.example.json
|
||||
rg -q '`agent_zone_id` is the optional' agents/windows/README.md
|
||||
! rg -q 'commands/pending\?agent_id=' "${service}"
|
||||
! rg -q 'DefaultValue "super"' "${service}"
|
||||
rg -q 'get\.miner\.setting' "${control}"
|
||||
|
||||
Reference in new issue
Block a user