Harden RF node control and agent updates

This commit is contained in:
LittleSam129
2026-09-30 11:42:19 +03:00
parent a94f2db4ee
commit 0d6fff224e
34 changed files with 14010 additions and 0 deletions

View File

@@ -0,0 +1,429 @@
"""Software-only checks for transactional bootstrap target deployment."""
from __future__ import annotations
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
import pytest
BOOTSTRAP_ID = "rf001-bootstrap-2026.09.29.4"
RELEASE_ID = "rf001-agent-2026.09.29.1"
AGENT_VERSION = "2026.09.29.1"
RELEASE_PREFIX = f"versions/{RELEASE_ID}/"
FUNCTIONS = (
"Get-LowerSha256",
"Assert-ExactReleasePath",
"Get-ReleaseDirectoryState",
"Assert-ReleaseMetadata",
"New-UniqueQuarantineDestination",
"Write-CreateNewUtf8Json",
"Move-ConflictingTargetToQuarantine",
"Move-FailedReleasePathToQuarantine",
"Install-CleanRelease",
"Prepare-TargetRelease",
)
def _powershell() -> str:
executable = shutil.which("powershell.exe")
if executable is None:
pytest.skip("Windows PowerShell is required for installer transaction tests")
return executable
def _installer() -> Path:
return Path(__file__).parents[1] / "tools" / "install_rf001_rx_autostart.ps1"
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
while block := stream.read(1024 * 1024):
digest.update(block)
return digest.hexdigest()
def _release_fixture(tmp_path: Path) -> tuple[Path, Path, Path, Path]:
install_root = tmp_path / "install"
versions_root = install_root / "versions"
target = versions_root / RELEASE_ID
source = tmp_path / "signed-bootstrap" / "versions" / RELEASE_ID
tools = source / "app" / "tools"
tools.mkdir(parents=True)
(tools / "rf001_rx_http_agent.py").write_text(
f'AGENT_VERSION = "{AGENT_VERSION}"\n', encoding="utf-8"
)
(tools / "rf001_agent_update.py").write_text(
"# fixed signed updater fixture\n", encoding="utf-8"
)
ready = {
"agent_version": AGENT_VERSION,
"bootstrap_id": BOOTSTRAP_ID,
"hardware_accessed": False,
"protocol_version": 1,
"release_id": RELEASE_ID,
"rf_tx": 0,
"schema_version": 1,
"status": "READY",
}
(source / "READY.json").write_text(
json.dumps(ready, sort_keys=True, separators=(",", ":")) + "\n",
encoding="utf-8",
)
rows = []
for path in sorted(item for item in source.rglob("*") if item.is_file()):
relative = path.relative_to(source).as_posix()
rows.append(
{
"path": RELEASE_PREFIX + relative,
"bytes": path.stat().st_size,
"sha256": _sha256(path),
}
)
manifest = tmp_path / "release_rows.json"
manifest.write_text(json.dumps(rows), encoding="utf-8")
versions_root.mkdir(parents=True)
return source, install_root, target, manifest
def _function_loader() -> str:
names = ",".join(f'"{name}"' for name in FUNCTIONS)
return rf"""
$ErrorActionPreference = "Stop"
$tokens = $null
$parseErrors = $null
$syntax = [Management.Automation.Language.Parser]::ParseFile(
$env:RF001_INSTALLER,
[ref]$tokens,
[ref]$parseErrors
)
if ($parseErrors.Count -ne 0) {{
throw "Installer contains PowerShell parse errors"
}}
foreach ($name in @({names})) {{
$matches = @($syntax.FindAll({{
param($node)
$node -is [Management.Automation.Language.FunctionDefinitionAst] -and
$node.Name -eq $name
}}, $true))
if ($matches.Count -ne 1) {{
throw "Installer function is absent or ambiguous: $name"
}}
. ([scriptblock]::Create($matches[0].Extent.Text))
}}
$releaseRows = @(Get-Content -LiteralPath $env:RF001_ROWS -Raw -Encoding UTF8 | ConvertFrom-Json)
$installRoot = $env:RF001_INSTALL_ROOT
$versionsRoot = Join-Path $installRoot "versions"
$sourceRelease = $env:RF001_SOURCE_RELEASE
$targetRelease = Join-Path $versionsRoot "{RELEASE_ID}"
"""
def _run_powershell(
body: str,
*,
source: Path,
install_root: Path,
manifest: Path,
check: bool = True,
) -> subprocess.CompletedProcess[str]:
environment = os.environ.copy()
environment.update(
{
"RF001_INSTALLER": str(_installer()),
"RF001_ROWS": str(manifest),
"RF001_INSTALL_ROOT": str(install_root),
"RF001_SOURCE_RELEASE": str(source),
}
)
completed = subprocess.run(
[_powershell(), "-NoProfile", "-NonInteractive", "-Command", _function_loader() + body],
check=False,
capture_output=True,
encoding="utf-8",
env=environment,
)
if check and completed.returncode != 0:
raise AssertionError(
f"PowerShell transaction harness failed ({completed.returncode}):\n"
f"STDOUT:\n{completed.stdout}\nSTDERR:\n{completed.stderr}"
)
return completed
def _prepare(
source: Path,
install_root: Path,
manifest: Path,
) -> dict[str, object]:
body = rf"""
$result = Prepare-TargetRelease `
-InstallRoot $installRoot `
-VersionsRoot $versionsRoot `
-SourceRelease $sourceRelease `
-TargetRelease $targetRelease `
-ReleaseId "{RELEASE_ID}" `
-BootstrapId "{BOOTSTRAP_ID}" `
-AgentVersion "{AGENT_VERSION}" `
-ProtocolVersion 1 `
-ReleaseRows $releaseRows `
-ReleasePrefix "{RELEASE_PREFIX}"
$final = Get-ReleaseDirectoryState `
-TargetRelease $targetRelease `
-ReleaseRows $releaseRows `
-ReleasePrefix "{RELEASE_PREFIX}"
[ordered]@{{
result = $result
final_state = $final.state
}} | ConvertTo-Json -Depth 10 -Compress
"""
completed = _run_powershell(
body,
source=source,
install_root=install_root,
manifest=manifest,
)
value = json.loads(completed.stdout.lstrip("\ufeff"))
assert isinstance(value, dict)
return value
def _snapshot(root: Path) -> dict[str, tuple[int, int, str]]:
return {
path.relative_to(root).as_posix(): (
path.stat().st_size,
path.stat().st_mtime_ns,
_sha256(path),
)
for path in root.rglob("*")
if path.is_file()
}
def _assert_matches_manifest(target: Path, manifest: Path) -> None:
rows = json.loads(manifest.read_text(encoding="utf-8"))
expected = {
str(row["path"])[len(RELEASE_PREFIX) :]: (
int(row["bytes"]),
str(row["sha256"]),
)
for row in rows
}
actual = {
path.relative_to(target).as_posix(): (path.stat().st_size, _sha256(path))
for path in target.rglob("*")
if path.is_file()
}
assert actual == expected
def test_absent_target_is_deployed_from_signed_manifest(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
report = _prepare(source, install_root, manifest)
assert report["result"]["initial_state"] == "TARGET_ABSENT"
assert report["result"]["reused"] is False
assert report["final_state"] == "TARGET_VERIFIED"
_assert_matches_manifest(target, manifest)
def test_exact_target_is_reused_without_writing_bytes(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
shutil.copytree(source, target)
before = _snapshot(target)
report = _prepare(source, install_root, manifest)
assert report["result"]["initial_state"] == "TARGET_VERIFIED"
assert report["result"]["reused"] is True
assert _snapshot(target) == before
def test_mixed_release_is_quarantined_before_clean_deployment(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
shutil.copytree(source, target)
agent = target / "app" / "tools" / "rf001_rx_http_agent.py"
agent.write_text('AGENT_VERSION = "2026.09.29.2"\n', encoding="utf-8")
ready = json.loads((target / "READY.json").read_text(encoding="utf-8"))
ready["bootstrap_id"] = "rf001-bootstrap-2026.09.29.2"
(target / "READY.json").write_text(json.dumps(ready), encoding="utf-8")
conflict_hash = _sha256(agent)
report = _prepare(source, install_root, manifest)
result = report["result"]
assert result["initial_state"] == "TARGET_CONFLICT"
quarantine = Path(str(result["quarantine_path"]))
diagnostic_path = Path(str(result["quarantine_diagnostic"]))
assert quarantine.is_dir()
assert _sha256(quarantine / "app" / "tools" / agent.name) == conflict_hash
diagnostic = json.loads(diagnostic_path.read_text(encoding="utf-8-sig"))
assert diagnostic["bootstrap_id"] == BOOTSTRAP_ID
assert diagnostic["expected_release_id"] == RELEASE_ID
assert {row["kind"] for row in diagnostic["mismatches"]} >= {
"SHA256_MISMATCH"
}
assert any(row["sha256"] == conflict_hash for row in diagnostic["conflicting_files"])
_assert_matches_manifest(target, manifest)
@pytest.mark.parametrize(
("case", "expected_kind"),
(
("ready_mismatch", "SHA256_MISMATCH"),
("python_mismatch", "SHA256_MISMATCH"),
("missing", "MISSING"),
("unexpected", "UNEXPECTED"),
),
)
def test_each_file_set_conflict_is_quarantined(
tmp_path: Path,
case: str,
expected_kind: str,
) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
shutil.copytree(source, target)
if case == "ready_mismatch":
(target / "READY.json").write_bytes(b"{}\n")
elif case == "python_mismatch":
(target / "app" / "tools" / "rf001_agent_update.py").write_bytes(b"changed\n")
elif case == "missing":
(target / "app" / "tools" / "rf001_agent_update.py").unlink()
else:
(target / "app" / "tools" / "unexpected.py").write_bytes(b"unexpected\n")
result = _prepare(source, install_root, manifest)["result"]
diagnostic = json.loads(
Path(str(result["quarantine_diagnostic"])).read_text(encoding="utf-8-sig")
)
assert expected_kind in {row["kind"] for row in diagnostic["mismatches"]}
_assert_matches_manifest(target, manifest)
def test_failure_after_quarantine_leaves_canonical_target_absent(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
shutil.copytree(source, target)
(target / "READY.json").write_bytes(b"conflict\n")
body = rf"""
$state = Get-ReleaseDirectoryState `
-TargetRelease $targetRelease `
-ReleaseRows $releaseRows `
-ReleasePrefix "{RELEASE_PREFIX}"
$quarantine = Move-ConflictingTargetToQuarantine `
-InstallRoot $installRoot `
-VersionsRoot $versionsRoot `
-TargetRelease $targetRelease `
-ReleaseId "{RELEASE_ID}" `
-BootstrapId "{BOOTSTRAP_ID}" `
-State $state
try {{
throw "fixed failure after quarantine before target creation"
}}
catch {{
[ordered]@{{
error = $_.Exception.Message
canonical_exists = Test-Path -LiteralPath $targetRelease
quarantine_exists = Test-Path -LiteralPath $quarantine.path -PathType Container
diagnostic_exists = Test-Path -LiteralPath $quarantine.diagnostic_path -PathType Leaf
}} | ConvertTo-Json -Compress
}}
"""
completed = _run_powershell(
body, source=source, install_root=install_root, manifest=manifest
)
report = json.loads(completed.stdout.lstrip("\ufeff"))
assert report == {
"error": "fixed failure after quarantine before target creation",
"canonical_exists": False,
"quarantine_exists": True,
"diagnostic_exists": True,
}
def test_failure_during_deployment_quarantines_partial_staging(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
(source / "READY.json").unlink()
body = rf"""
try {{
Install-CleanRelease `
-InstallRoot $installRoot `
-VersionsRoot $versionsRoot `
-SourceRelease $sourceRelease `
-TargetRelease $targetRelease `
-ReleaseId "{RELEASE_ID}" `
-BootstrapId "{BOOTSTRAP_ID}" `
-AgentVersion "{AGENT_VERSION}" `
-ProtocolVersion 1 `
-ReleaseRows $releaseRows `
-ReleasePrefix "{RELEASE_PREFIX}" | Out-Null
throw "deployment unexpectedly succeeded"
}}
catch {{
$failed = @(Get-ChildItem -LiteralPath (Join-Path $installRoot "quarantine") -Directory |
Where-Object {{ $_.Name -like "{RELEASE_ID}_failed_staging_*" }})
[ordered]@{{
error = $_.Exception.Message
canonical_exists = Test-Path -LiteralPath $targetRelease
failed_staging_count = $failed.Count
}} | ConvertTo-Json -Compress
}}
"""
completed = _run_powershell(
body, source=source, install_root=install_root, manifest=manifest
)
report = json.loads(completed.stdout.lstrip("\ufeff"))
assert "Clean release deployment failed" in report["error"]
assert report["canonical_exists"] is False
assert report["failed_staging_count"] == 1
def test_installer_switch_failure_restores_saved_task_and_launcher_state() -> None:
text = _installer().read_text(encoding="utf-8-sig")
switch = text.index("Set-ScheduledTask -TaskName $taskName -Action $action")
catch = text.index("catch {", switch)
restore_task = text.index("Register-ScheduledTask -TaskName $taskName -Xml $xml -Force", catch)
restore_files = text.index('foreach ($name in @("launcher"', catch)
restart_old = text.index("if ($wasRunning)", catch)
assert switch < catch < restore_task < restore_files < restart_old
def test_successful_reinstall_is_idempotent(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
first = _prepare(source, install_root, manifest)
before = _snapshot(target)
second = _prepare(source, install_root, manifest)
assert first["result"]["initial_state"] == "TARGET_ABSENT"
assert second["result"]["initial_state"] == "TARGET_VERIFIED"
assert second["result"]["reused"] is True
assert _snapshot(target) == before
quarantine = install_root / "quarantine"
assert not quarantine.exists() or not any(quarantine.iterdir())
def test_existing_quarantine_is_never_overwritten(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
shutil.copytree(source, target)
(target / "READY.json").write_bytes(b"conflict\n")
sentinel = install_root / "quarantine" / "existing-audit" / "sentinel.bin"
sentinel.parent.mkdir(parents=True)
sentinel.write_bytes(b"must remain unchanged")
before = _snapshot(sentinel.parent)
result = _prepare(source, install_root, manifest)["result"]
assert _snapshot(sentinel.parent) == before
assert Path(str(result["quarantine_path"])) != sentinel.parent
def test_matching_version_strings_do_not_validate_changed_bytes(tmp_path: Path) -> None:
source, install_root, target, manifest = _release_fixture(tmp_path)
shutil.copytree(source, target)
agent = target / "app" / "tools" / "rf001_rx_http_agent.py"
agent.write_text(
f'# changed bytes, same identity strings\nAGENT_VERSION = "{AGENT_VERSION}"\n',
encoding="utf-8",
)
result = _prepare(source, install_root, manifest)["result"]
assert result["initial_state"] == "TARGET_CONFLICT"
assert result["reused"] is False
_assert_matches_manifest(target, manifest)