ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
fleet_wsl.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Stage and run declared fleet plays inside a Windows host's WSL distro."""
4
5from __future__ import annotations
6
7import base64
8import hashlib
9import json
10import os
11import shlex
12import subprocess
13import sys
14import tempfile
15import textwrap
16from dataclasses import dataclass
17from pathlib import Path
18from typing import Any, Protocol
19
20import fleet_model as fm
21import fleet_reach as fr
22import fleet_typed_vars as ftv
23import fleet_wsl_stage as fws
24
25# Under the distro's ext4 root, never /mnt/c: drvfs is much slower for the
26# many-small-files work performed by Ansible and the runner image.
27WSL_STAGE = fws.WSL_STAGE
28WSL_RUNNER_IMAGE_CACHE = fws.WSL_RUNNER_IMAGE_CACHE
29WSL_MANAGED_ROOT = "/opt/ra8-python-tools"
30WSL_MANAGED_CACHE = "/opt/ra8-python-tools-cache"
31WSL_ANSIBLE_PLAYBOOK = "/opt/ra8-python-tools/bin/ansible-playbook"
32WSL_SYSTEM_PYTHON = "/usr/bin/python3"
33APPLY_REQUIRED_STATUS = 4
34FATAL_VERIFIER_STATUS = 2
35FAKE_ANSIBLE_FAILURE_STATUS = 23
36FAKE_UV_FAILURE_STATUS = 31
37FAKE_UV_ARGV_FAILURE_STATUS = 93
38
39
40class CommandRunner(Protocol):
41 """Signature of fleet.py's streaming command runner."""
42
43 def __call__(
44 self,
45 argv: list[str],
46 stdin: str | bytes | None = None,
47 cwd: Path | None = None,
48 ) -> int:
49 """Run one command with optional streamed stdin and working directory."""
50 ...
51
52
53def _fail(message: str) -> int:
54 """Print one transport failure and return the fleet precondition status."""
55 print(f"fleet: error: {message}", file=sys.stderr)
56 return 2
57
58
59def _command_output(argv: list[str]) -> tuple[int, str]:
60 """Run a read-only probe and return its status and stripped stdout."""
61 proc = subprocess.run( # noqa: S603 -- argv comes from the validated declaration
62 argv,
63 text=True,
64 capture_output=True,
65 check=False,
66 )
67 if proc.returncode:
68 sys.stderr.write(proc.stderr)
69 return proc.returncode, proc.stdout.strip()
70
71
72@dataclass(frozen=True)
73class _RunnerImageTransfer:
74 """Carry one verified runner-image stream between declared fleet hosts."""
75
76 source_ssh: list[str]
77 target_ssh: list[str]
78 target: dict[str, Any]
79 distro: str
80 source_archive: str
81 run: CommandRunner
82
83
84def _stream_runner_image(transfer: _RunnerImageTransfer) -> int:
85 """Stream one canonical archive into the WSL cache's staging path."""
86 source_ssh = transfer.source_ssh
87 target_ssh = transfer.target_ssh
88 target = transfer.target
89 distro = transfer.distro
90 source_archive = transfer.source_archive
91 run = transfer.run
92 source = subprocess.Popen( # noqa: S603 -- declaration-derived argv, no shell
93 [*source_ssh, "/usr/bin/sudo", "-n", "/usr/bin/cat", "--", source_archive],
94 stdout=subprocess.PIPE,
95 )
96 if source.stdout is None: # pragma: no cover -- PIPE guarantees this
97 source.terminate()
98 return _fail("could not open the canonical archive stream")
99 receive = fws.cache_receive_command(distro)
100 sink = subprocess.Popen( # noqa: S603 -- declaration-derived argv, no shell
101 [*target_ssh, receive], stdin=source.stdout
102 )
103 source.stdout.close()
104 sink_rc = sink.wait()
105 source_rc = source.wait()
106 if source_rc or sink_rc:
107 run(
108 [*target_ssh, fm.remote_shell(target)],
109 stdin=fws.cache_cleanup_script(),
110 )
111 return _fail(f"runner image stream failed (source rc={source_rc}, target rc={sink_rc})")
112 return 0
113
114
115def _sync_runner_image(data: dict[str, Any], name: str, run: CommandRunner) -> int:
116 """Stream the canonical runner archive into a WSL-local durable cache."""
117 image = data["runner_image"]
118 source_name = str(image["source_host"])
119 source_archive = str(image["archive"])
120 source_ssh = fr.ssh_target(data, source_name)
121 target_ssh = fr.ssh_target(data, name)
122 target = data["hosts"][name]
123 distro = str(target["connect"]["distro"])
124 rc, source_line = _command_output(
125 [*source_ssh, "/usr/bin/sudo", "-n", "/usr/bin/sha256sum", "--", source_archive]
126 )
127 if rc or not source_line:
128 return _fail(f"cannot checksum canonical runner archive on {source_name}")
129 source_sha = source_line.split()[0]
130 rc = run(
131 [*target_ssh, fm.remote_shell(target)],
132 stdin=fws.cache_prepare_script(),
133 )
134 if rc:
135 return rc
136 cache_probe = (
137 f"wsl -d {shlex.quote(distro)} -u root -e /usr/bin/env -i "
138 "HOME=/root PATH=/usr/bin:/bin /usr/bin/sha256sum -- "
139 f"{shlex.quote(WSL_RUNNER_IMAGE_CACHE)}"
140 )
141 cache_rc, cache_line = _command_output([*target_ssh, cache_probe])
142 if cache_rc == 0 and cache_line.split()[0] == source_sha:
143 print(f"==> WSL runner image cache already matches {source_name} ({source_sha[:12]})")
144 return 0
145 print(f"==> streaming canonical runner image {source_name} -> {name}")
146 transfer = _RunnerImageTransfer(
147 source_ssh,
148 target_ssh,
149 target,
150 distro,
151 source_archive,
152 run,
153 )
154 rc = _stream_runner_image(transfer)
155 if rc:
156 return rc
157 return run(
158 [*target_ssh, fm.remote_shell(target)],
159 stdin=fws.cache_publish_script(source_sha),
160 )
161
162
163@dataclass(frozen=True)
164class ConvergeSpec:
165 """Describe one validated WSL converge without exposing secret values."""
166
167 data: dict[str, Any]
168 name: str
169 plays: list[str]
170 extra: list[str]
171 typed_vars: ftv.TypedVars | None
172 mode: str
173 stage: str = WSL_STAGE
174 ansible_playbook: str = WSL_ANSIBLE_PLAYBOOK
175 system_python: str = WSL_SYSTEM_PYTHON
176 managed_root: str = WSL_MANAGED_ROOT
177 managed_cache: str = WSL_MANAGED_CACHE
178
179
180def _isolation_lines() -> list[str]:
181 """Render the inherited environment scrubbing boundary."""
182 return [
183 "set -euo pipefail",
184 "while IFS='=' read -r name _; do",
185 ' case "$name" in ANSIBLE_*) unset "$name" ;; esac',
186 ' case "$name" in PYTHONHOME|PYTHONPATH|PYTHONNOUSERSITE) unset "$name" ;; esac',
187 ' case "$name" in UV_*) unset "$name" ;; esac',
188 "done < <(env)",
189 "export PYTHONNOUSERSITE=1",
190 ]
191
192
193def _managed_authority_paths(stage: str) -> tuple[tuple[str, ...], tuple[str, ...]]:
194 """Return the managed directories and files proven by the WSL payload."""
195 paths = (
196 stage,
197 f"{stage}/infra",
198 f"{stage}/infra/ansible",
199 f"{stage}/.ansible",
200 f"{stage}/.ansible/collections",
201 f"{stage}/.tools",
202 f"{stage}/.tools/uv",
203 )
204 files = (
205 f"{stage}/infra/ansible/ansible.cfg",
206 f"{stage}/infra/ansible/requirements.yml",
207 f"{stage}/pyproject.toml",
208 f"{stage}/uv.lock",
209 f"{stage}/scripts/dev/bootstrap_uv.py",
210 f"{stage}/scripts/dev/bootstrap_uv_exec.py",
211 f"{stage}/scripts/dev/fleet_runner_maintenance.py",
212 f"{stage}/scripts/dev/fleet_path_authority.py",
213 f"{stage}/scripts/dev/uv_release.json",
214 f"{stage}/scripts/dev/verify_locked_environment.py",
215 f"{stage}/scripts/checks/check_ansible_collections.py",
216 )
217 return paths, files
218
219
220def _proof_function_lines() -> list[str]:
221 """Render reusable no-link, metadata, mount, and durability shell helpers."""
222 return [
223 "require_real_dir() {",
224 ' [ -d "$1" ] && [ ! -L "$1" ] && [ "$(readlink -f -- "$1")" = "$1" ] || {',
225 ' echo "unsafe or missing managed directory: $1" >&2; exit 1;',
226 " }",
227 "}",
228 "require_exact_dir() {",
229 ' require_real_dir "$1"',
230 ' [ "$(stat -c %u -- "$1")" = "$authority_uid" ] &&',
231 ' [ "$(stat -c %a -- "$1")" = 755 ] || {',
232 ' echo "unsafe managed directory metadata: $1" >&2; exit 1;',
233 " }",
234 "}",
235 "require_real_file() {",
236 ' [ -f "$1" ] && [ ! -L "$1" ] && [ "$(readlink -f -- "$1")" = "$1" ] || {',
237 ' echo "unsafe or missing managed file: $1" >&2; exit 1;',
238 " }",
239 "}",
240 "require_managed_python() {",
241 ' [ -L "$1" ] && [ "$(readlink -- "$1")" = python ] || {',
242 ' echo "unsafe managed Python entry: $1" >&2; exit 1;',
243 " }",
244 ' python_link="$(dirname -- "$1")/python"',
245 ' [ -L "$python_link" ] && [ "$(readlink -- "$python_link")" = "$2" ] &&',
246 ' [ "$(readlink -f -- "$1")" = "$(readlink -f -- "$2")" ] || {',
247 ' echo "managed Python does not resolve to its pinned system interpreter" >&2; exit 1;',
248 " }",
249 "}",
250 "require_exact_file() {",
251 ' require_real_file "$1"',
252 ' [ "$(stat -c %a -- "$1")" = "$2" ] || {',
253 ' echo "wrong managed file mode: $1" >&2; exit 1;',
254 " }",
255 ' file_digest="$(sha256sum -- "$1")"',
256 ' [ "${file_digest%% *}" = "$3" ] || {',
257 ' echo "changed managed file bytes: $1" >&2; exit 1;',
258 " }",
259 "}",
260 "refuse_mount() {",
261 ' ! /usr/bin/mountpoint -q -- "$1" || {',
262 ' echo "refusing managed mount point: $1" >&2; exit 1;',
263 " }",
264 "}",
265 "sync_file() {",
266 " /usr/bin/python3 -I -S -c 'import os,sys; "
267 "f=os.open(sys.argv[1],os.O_RDONLY|os.O_NOFOLLOW); "
268 'os.fsync(f); os.close(f)\' "$1"',
269 "}",
270 "sync_dir() {",
271 " /usr/bin/python3 -I -S -c 'import os,sys; "
272 "f=os.open(sys.argv[1],os.O_RDONLY|os.O_DIRECTORY); "
273 'os.fsync(f); os.close(f)\' "$1"',
274 "}",
275 ]
276
277
278def _path_proof_lines(stage: str, managed_root: str, managed_cache: str) -> list[str]:
279 """Render exact no-link checks for every executable authority."""
280 source_root = fm.REPO_ROOT if stage == WSL_STAGE else Path(stage)
281 bootstrap = source_root / "scripts/dev/bootstrap_uv.py"
282 helper = source_root / "scripts/dev/bootstrap_uv_exec.py"
283 bootstrap_digest = hashlib.sha256(bootstrap.read_bytes()).hexdigest()
284 helper_digest = hashlib.sha256(helper.read_bytes()).hexdigest()
285 paths, files = _managed_authority_paths(stage)
286 return [
287 *_proof_function_lines(),
288 *[f"require_real_dir {shlex.quote(path)}" for path in paths],
289 *[f"require_real_file {shlex.quote(path)}" for path in files],
290 f"require_exact_file {shlex.quote(stage + '/' + fws.OWNER_FILE)} 644 "
291 f"{hashlib.sha256((fws.STAGE_OWNER + chr(10)).encode('ascii')).hexdigest()}",
292 f"require_exact_file {shlex.quote(stage + '/scripts/dev/bootstrap_uv.py')} "
293 f"755 {bootstrap_digest}",
294 f"require_exact_file {shlex.quote(stage + '/scripts/dev/bootstrap_uv_exec.py')} "
295 f"644 {helper_digest}",
296 f"authority_uid=$(stat -c %u -- {shlex.quote(stage)})",
297 f"managed_root={shlex.quote(managed_root)}",
298 f"managed_cache={shlex.quote(managed_cache)}",
299 'require_real_dir "$(dirname "$managed_root")"',
300 'require_real_dir "$(dirname "$managed_cache")"',
301 ]
302
303
304def _apply_environment_lines(sync_flags: str) -> list[str]:
305 """Render the mutable managed-environment half of a toolchain sync."""
306 return [
307 ' if [ -e "$managed_root" ] || [ -L "$managed_root" ]; then',
308 ' require_exact_dir "$managed_root"',
309 ' refuse_mount "$managed_root"',
310 " else",
311 ' install -d -m 0755 -- "$managed_root"',
312 ' refuse_mount "$managed_root"',
313 " fi",
314 ' if [ -e "$managed_cache" ] || [ -L "$managed_cache" ]; then',
315 ' require_exact_dir "$managed_cache"',
316 ' refuse_mount "$managed_cache"',
317 " else",
318 ' install -d -m 0755 -- "$managed_cache"',
319 ' refuse_mount "$managed_cache"',
320 " fi",
321 ' UV_PROJECT_ENVIRONMENT="$managed_root" UV_PYTHON_DOWNLOADS=never '
322 'UV_CACHE_DIR="$managed_cache" uv_run '
323 f"{sync_flags}",
324 ]
325
326
327def _check_environment_lines(sync_flags: str) -> list[str]:
328 """Render safe-drift classification and read-only environment verification."""
329 return [
330 ' if [ ! -e "$managed_root" ] && [ ! -L "$managed_root" ]; then',
331 f' echo "managed WSL Python environment is missing; apply required" >&2; '
332 f"exit {fws.REMOTE_APPLY_REQUIRED_STATUS}",
333 " fi",
334 ' require_exact_dir "$managed_root"',
335 ' refuse_mount "$managed_root"',
336 ' lock_marker="$managed_root/.ra8-infra-lock.sha256"',
337 ' if [ ! -e "$lock_marker" ] && [ ! -L "$lock_marker" ]; then',
338 f' echo "managed WSL Python environment is stale; apply required" >&2; '
339 f"exit {fws.REMOTE_APPLY_REQUIRED_STATUS}",
340 " fi",
341 ' [ -f "$lock_marker" ] && [ ! -L "$lock_marker" ] &&',
342 ' [ "$(stat -c %u -- "$lock_marker")" = "$authority_uid" ] &&',
343 ' [ "$(stat -c %a -- "$lock_marker")" = 644 ] || {',
344 ' echo "unsafe managed WSL Python environment marker" >&2; exit 1;',
345 " }",
346 ' if [ "$(cat "$lock_marker")" != "$authority_digest" ]; then',
347 f' echo "managed WSL Python environment is stale; apply required" >&2; '
348 f"exit {fws.REMOTE_APPLY_REQUIRED_STATUS}",
349 " fi",
350 ' if [ ! -e "$managed_cache" ] && [ ! -L "$managed_cache" ]; then',
351 f' echo "managed WSL cache is missing; apply required" >&2; '
352 f"exit {fws.REMOTE_APPLY_REQUIRED_STATUS}",
353 " fi",
354 ' require_exact_dir "$managed_cache"',
355 ' refuse_mount "$managed_cache"',
356 " sync_status=0",
357 ' UV_PROJECT_ENVIRONMENT="$managed_root" UV_PYTHON_DOWNLOADS=never '
358 'UV_CACHE_DIR="$managed_cache" uv_run --offline --no-cache '
359 f"{sync_flags} --check || sync_status=$?",
360 ' if [ "$sync_status" -eq 1 ]; then',
361 f' echo "managed WSL Python environment differs; apply required" >&2; '
362 f"exit {fws.REMOTE_APPLY_REQUIRED_STATUS}",
363 " fi",
364 ' [ "$sync_status" -eq 0 ] || exit "$sync_status"',
365 ]
366
367
368def _toolchain_sync_lines(stage: str, mode: str, system_python: str) -> list[str]:
369 """Render apply-only lock sync and read-only verification for other modes."""
370 if mode not in {"apply", "check", "remove"}:
371 message = f"unsupported WSL convergence mode: {mode}"
372 raise ValueError(message)
373 bootstrap = f"{stage}/scripts/dev/bootstrap_uv.py"
374 manifest = f"{stage}/scripts/dev/uv_release.json"
375 uv_cache = f"{stage}/.tools/uv"
376 sync_flags = (
377 f"--no-config --directory {shlex.quote(stage)} sync --locked --only-group infra "
378 "--no-install-project "
379 f"--python {shlex.quote(system_python)}"
380 )
381 return [
382 f"mode={shlex.quote(mode)}",
383 f"cd {shlex.quote(stage)}",
384 "uv_run() {",
385 f" {shlex.quote(system_python)} {shlex.quote(bootstrap)} "
386 f"--manifest {shlex.quote(manifest)} --cache-root {shlex.quote(uv_cache)} "
387 '--run "$@"',
388 "}",
389 'authority_digest="$(sha256sum -- '
390 f"{shlex.quote(stage + '/pyproject.toml')} {shlex.quote(stage + '/uv.lock')} "
391 f"{shlex.quote(manifest)} {shlex.quote(stage + '/infra/ansible/requirements.yml')} "
392 '| sha256sum)"',
393 "authority_digest=${authority_digest%% *}",
394 'if [ "$mode" = apply ]; then',
395 *_apply_environment_lines(sync_flags),
396 "else",
397 *_check_environment_lines(sync_flags),
398 "fi",
399 ]
400
401
402def _toolchain_verify_lines(stage: str, ansible_playbook: str, system_python: str) -> list[str]:
403 """Render exact-set, collection, and durable-authority verification."""
404 verifier = f"{stage}/scripts/dev/verify_locked_environment.py"
405 collection_checker = f"{stage}/scripts/checks/check_ansible_collections.py"
406 return [
407 f'require_managed_python "$managed_root/bin/python3" {shlex.quote(system_python)}',
408 f"require_real_file {shlex.quote(ansible_playbook)}",
409 'require_real_file "$managed_root/bin/ansible-galaxy"',
410 "(",
411 " pipeline_status=(0 0)",
412 " uv_run --no-config --directory "
413 + shlex.quote(stage)
414 + " export --locked --offline --only-group infra "
415 "--no-emit-project --no-header | "
416 '"$managed_root/bin/python3" '
417 + shlex.quote(verifier)
418 + ' /dev/stdin || pipeline_status=("${PIPESTATUS[@]}")',
419 ' [ "${pipeline_status[0]}" -eq 0 ] || exit "${pipeline_status[0]}"',
420 ' if [ "${pipeline_status[1]}" -eq 1 ] && [ "$mode" = check ]; then',
421 f' echo "managed WSL package set differs; apply required" >&2; '
422 f"exit {fws.REMOTE_APPLY_REQUIRED_STATUS}",
423 " fi",
424 ' [ "${pipeline_status[1]}" -eq 0 ] || exit "${pipeline_status[1]}"',
425 ")",
426 "ANSIBLE_COLLECTIONS_PATH=" + shlex.quote(stage + "/.ansible/collections") + " "
427 '"$managed_root/bin/ansible-galaxy" collection list --format json | '
428 '"$managed_root/bin/python3" '
429 + shlex.quote(collection_checker)
430 + " --stdin --root "
431 + shlex.quote(stage + "/.ansible/collections"),
432 'if [ "$mode" = apply ]; then',
433 ' marker="$managed_root/.ra8-infra-lock.sha256.tmp.$$"',
434 " trap 'rm -f -- \"$marker\"' EXIT",
435 ' printf \'%s\\n\' "$authority_digest" >"$marker"',
436 ' chmod 0644 "$marker"',
437 ' mv -f -- "$marker" "$managed_root/.ra8-infra-lock.sha256"',
438 ' sync_file "$managed_root/.ra8-infra-lock.sha256"',
439 ' sync_dir "$managed_root"',
440 " trap - EXIT",
441 "fi",
442 f"cd {shlex.quote(stage + '/infra/ansible')}",
443 'export ANSIBLE_CONFIG="$PWD/ansible.cfg"',
444 'export ANSIBLE_COLLECTIONS_PATH="$PWD/../../.ansible/collections"',
445 "export ANSIBLE_COLLECTIONS_SCAN_SYS_PATH=false",
446 ]
447
448
449def _ansible_environment_lines(spec: ConvergeSpec) -> list[str]:
450 """Render the exact remote managed-tool and Ansible boundary."""
451 return [
452 *_isolation_lines(),
453 *_path_proof_lines(spec.stage, spec.managed_root, spec.managed_cache),
454 *_toolchain_sync_lines(spec.stage, spec.mode, spec.system_python),
455 *_toolchain_verify_lines(spec.stage, spec.ansible_playbook, spec.system_python),
456 ]
457
458
459def render_converge(spec: ConvergeSpec) -> tuple[str, list[str]]:
460 """Render the stdin script and secret-free summaries for a WSL converge."""
461 data = spec.data
462 name = spec.name
463 plays = spec.plays
464 extra = spec.extra
465 typed_vars = spec.typed_vars
466 stage = spec.stage
467 ansible_playbook = spec.ansible_playbook
468 role_variables = fm.role_vars(data, name, data["hosts"][name])
469 role_variables["ci_runner_docker_image_source_local_archive"] = WSL_RUNNER_IMAGE_CACHE
470 role_variables["fleet_capacity_state_group"] = "root"
471 lines = _ansible_environment_lines(spec)
472 if typed_vars is not None:
473 encoded = base64.b64encode(typed_vars.content).decode("ascii")
474 lines.extend(["trap 'exit 130' INT", "trap 'exit 143' TERM HUP"])
475 if spec.mode == "check":
476 lines.extend(
477 [
478 "exec {ra8_vars_fd}< <(base64 -d <<'RA8_TYPED_VARS_EOF'",
479 *textwrap.wrap(encoded, width=76),
480 "RA8_TYPED_VARS_EOF",
481 ")",
482 'typed_args=(-e "@/dev/fd/$ra8_vars_fd")',
483 ]
484 )
485 else:
486 lines.extend(
487 [
488 "umask 077",
489 f'ra8_vars_file="$(mktemp {shlex.quote(stage + "/.ansible-vars.XXXXXX")})"',
490 'cleanup_ra8_vars() { rm -f -- "$ra8_vars_file"; }',
491 "trap cleanup_ra8_vars EXIT",
492 "base64 -d >\"$ra8_vars_file\" <<'RA8_TYPED_VARS_EOF'",
493 *textwrap.wrap(encoded, width=76),
494 "RA8_TYPED_VARS_EOF",
495 'typed_args=(-e "@$ra8_vars_file")',
496 ]
497 )
498 else:
499 lines.append("typed_args=()")
500 summaries: list[str] = []
501 for play in plays:
502 playbook = fm.PLAYS[play].playbook
503 argv = [
504 ansible_playbook,
505 "--connection=local",
506 "-i",
507 "localhost,",
508 f"playbooks/{playbook}",
509 "-e",
510 json.dumps(role_variables),
511 "-e",
512 f"wsl_ci_host_id={name}",
513 "-e",
514 f"fleet_capacity_src={stage}/scripts/ci/fleet_capacity.sh",
515 *extra,
516 ]
517 lines.append(shlex.join(argv) + ' "${typed_args[@]}"')
518 summaries.append(f"ansible-playbook {playbook} (WSL host {name})")
519 return "\n".join(lines) + "\n", summaries
520
521
522def converge(
523 spec: ConvergeSpec,
524 sync_image: bool,
525 run: CommandRunner,
526) -> int:
527 """Stage and run WSL plays without putting secret values in process argv."""
528 data = spec.data
529 name = spec.name
530 host = data["hosts"][name]
531 generation = ""
532 if spec.mode == "check":
533 rc = fws.verify_stage_sources("check")
534 if not rc:
535 generation = fws.stage_generation()
536 else:
537 rc, generation = fws.prepare(data, name, spec.mode, run)
538 if rc:
539 return rc
540 if sync_image:
541 rc = _sync_runner_image(data, name, run)
542 if rc:
543 run(
544 [*fr.ssh_target(data, name), fm.remote_shell(host)],
545 stdin=fws.stage_cleanup_script(),
546 )
547 return rc
548 converge_script, summaries = render_converge(spec)
549 prefix = ["set -euo pipefail", *fws.transaction_lock_lines(spec.mode != "check")]
550 if spec.mode == "check":
551 prefix.extend(fws.stage_probe_lines(generation))
552 else:
553 prefix.append(fws.stage_publish_script(generation=generation))
554 script = "\n".join([*prefix, converge_script])
555 for summary in summaries:
556 print(f"==> {summary}")
557 rc = run([*fr.ssh_target(data, name), fm.remote_shell(host)], stdin=script)
558 if spec.mode != "check":
559 run(
560 [*fr.ssh_target(data, name), fm.remote_shell(host)],
561 stdin=fws.stage_cleanup_script(),
562 )
563 if spec.mode == "check" and rc == fws.REMOTE_APPLY_REQUIRED_STATUS:
564 return APPLY_REQUIRED_STATUS
565 return rc
566
567
568@dataclass(frozen=True)
569class _SelftestFixture:
570 """Paths used to observe one offline WSL transport simulation."""
571
572 stage: Path
573 managed_root: Path
574 managed_cache: Path
575 system_python: Path
576 args_log: Path
577 vars_path_log: Path
578 mode_log: Path
579 uv_log: Path
580 python_log: Path
581 injected_path: Path
582 hostile_executed: Path
583
584
585def _write_executable(path: Path, source: str) -> None:
586 """Write one executable selftest helper."""
587 path.write_text(source, encoding="ascii")
588 path.chmod(0o755)
589
590
591def _remote_boundary_selftest(data: dict[str, Any], root: Path) -> list[str]:
592 """Prove BASH_ENV cannot execute before the streamed WSL payload."""
593 failures: list[str] = []
594 marker = root / "bash-env-ran"
595 startup = root / "hostile-bash-env"
596 startup.write_text(f"touch {shlex.quote(str(marker))}\n", encoding="ascii")
597 boundary = fm.remote_shell(data["hosts"]["win-ci"])
598 expected = "-u root -e /usr/bin/env -i HOME=/root PATH=/usr/bin:/bin /bin/bash -s"
599 if expected not in boundary:
600 failures.append("WSL actual remote boundary is not env-empty before Bash")
601 result = subprocess.run(
602 [
603 "/usr/bin/env",
604 "-i",
605 "HOME=/root",
606 "PATH=/usr/bin:/bin",
607 "/bin/bash",
608 "-s",
609 ],
610 input="true\n",
611 env={"BASH_ENV": str(startup), "PATH": "/hostile"},
612 text=True,
613 check=False,
614 )
615 if result.returncode or marker.exists():
616 failures.append("env-empty Bash boundary executed hostile BASH_ENV")
617 return failures
618
619
620def _write_stage_authorities(stage: Path) -> None:
621 """Create the exact authority shape consumed by the rendered shell."""
622 (stage / "infra" / "ansible").mkdir(parents=True)
623 (stage / ".ansible" / "collections").mkdir(parents=True)
624 (stage / ".tools" / "uv").mkdir(parents=True)
625 for relative, content in (
626 ("infra/ansible/ansible.cfg", "[defaults]\n"),
627 ("infra/ansible/requirements.yml", "collections: []\n"),
628 ("pyproject.toml", "[project]\nname='fixture'\n"),
629 ("uv.lock", "version = 1\n"),
630 ("scripts/dev/bootstrap_uv.py", "# fixture\n"),
631 ("scripts/dev/bootstrap_uv_exec.py", "# fixture\n"),
632 ("scripts/dev/fleet_runner_maintenance.py", "# fixture\n"),
633 ("scripts/dev/fleet_path_authority.py", "# fixture\n"),
634 ("scripts/dev/uv_release.json", "{}\n"),
635 ("scripts/dev/verify_locked_environment.py", "# fixture\n"),
636 ("scripts/checks/check_ansible_collections.py", "# fixture\n"),
637 ):
638 path = stage / relative
639 path.parent.mkdir(parents=True, exist_ok=True)
640 path.write_text(content, encoding="ascii")
641 (stage / "scripts/dev/bootstrap_uv.py").chmod(0o755)
642 (stage / "scripts/dev/bootstrap_uv_exec.py").chmod(0o644)
643 (stage / fws.OWNER_FILE).write_text(f"{fws.STAGE_OWNER}\n", encoding="ascii")
644
645
646def _write_fake_toolchain(root: Path, fixture: _SelftestFixture) -> None:
647 """Create fake uv/Python/Ansible programs with observation logs."""
648 managed_bin = fixture.managed_root / "bin"
649 managed_bin.mkdir(parents=True)
650 fake_uv = root / "verified-uv"
651 _write_executable(
652 fixture.system_python,
653 "#!/usr/bin/env bash\nset -eu\n"
654 '[ -z "${UV_CONFIG_FILE:-}" ]\n[ -z "${UV_INDEX_URL:-}" ]\n'
655 'case "$1" in\n'
656 " *verify_locked_environment.py) "
657 'printf \'%s\\n\' "$*" >>"$RA8_TEST_PYTHON_LOG"; cat >/dev/null; '
658 'exit "${RA8_TEST_VERIFY_STATUS:-0}" ;;\n'
659 " *check_ansible_collections.py) "
660 'printf \'%s\\n\' "$*" >>"$RA8_TEST_PYTHON_LOG"; cat >/dev/null; exit 0 ;;\n'
661 "esac\n"
662 '[ "$1" = "$RA8_TEST_STAGE/scripts/dev/bootstrap_uv.py" ]\nshift\n'
663 '[ "$1" = --manifest ]\n'
664 '[ "$2" = "$RA8_TEST_STAGE/scripts/dev/uv_release.json" ]\nshift 2\n'
665 '[ "$1" = --cache-root ]\n'
666 '[ "$2" = "$RA8_TEST_STAGE/.tools/uv" ]\nshift 2\n'
667 '[ "$1" = --run ]\nshift\n(($#))\n'
668 'exec "$RA8_TEST_UV" "$@"\n',
669 )
670 _write_executable(
671 fake_uv,
672 "#!/usr/bin/python3\n"
673 "import os\n"
674 "import sys\n"
675 "from pathlib import Path\n"
676 "stage = os.environ['RA8_TEST_STAGE']\n"
677 "args = sys.argv[1:]\n"
678 "if Path.cwd().resolve() != Path(stage).resolve():\n"
679 " raise SystemExit(92)\n"
680 "base = ['--no-config', '--directory', stage]\n"
681 "system_python = os.environ['RA8_TEST_SYSTEM_PYTHON']\n"
682 "sync = [*base, 'sync', '--locked', '--only-group', 'infra', "
683 "'--no-install-project', '--python', system_python]\n"
684 "check = ['--offline', '--no-cache', *sync, '--check']\n"
685 "export = [*base, 'export', '--locked', '--offline', '--only-group', "
686 "'infra', '--no-emit-project', '--no-header']\n"
687 "if args not in (sync, check, export):\n"
688 f" raise SystemExit({FAKE_UV_ARGV_FAILURE_STATUS})\n"
689 "operation = 'export' if args == export else 'sync'\n"
690 "with Path(os.environ['RA8_TEST_UV_LOG']).open('a', encoding='ascii') as stream:\n"
691 " stream.write('|'.join(args) + '\\n')\n"
692 "if os.environ.get('RA8_TEST_UV_FAIL') == operation:\n"
693 f" raise SystemExit({FAKE_UV_FAILURE_STATUS})\n"
694 "if os.environ.get('RA8_TEST_UV_FAIL') == 'sync-drift' and args == check:\n"
695 " raise SystemExit(1)\n"
696 "if operation == 'export':\n"
697 " for index in range(10):\n"
698 " print(f'package-{index}==1.{index} \\\\')\n"
699 " print(f' --hash=sha256:{index:064d}')\n",
700 )
701 (managed_bin / "python").symlink_to(fixture.system_python)
702 (managed_bin / "python3").symlink_to("python")
703 galaxy = managed_bin / "ansible-galaxy"
704 _write_executable(galaxy, "#!/usr/bin/env bash\nprintf '{}\\n'\n")
705
706
707def _write_fake_playbook(fixture: _SelftestFixture, sentinel: str) -> None:
708 """Create the managed failing Ansible executable used by the transport test."""
709 fake = fixture.managed_root / "bin" / "ansible-playbook"
710 _write_executable(
711 fake,
712 "#!/usr/bin/env bash\nset -eu\n"
713 ': >"$RA8_TEST_ARGS"\nvars_file=\nfor arg in "$@"; do\n'
714 ' printf \'%s\\n\' "$arg" >>"$RA8_TEST_ARGS"\n'
715 ' case "$arg" in @*) vars_file=${arg#@} ;; esac\ndone\n'
716 '[ -n "$vars_file" ]\n'
717 'printf \'%s\\n\' "$vars_file" >"$RA8_TEST_VARS_PATH"\n'
718 'stat -c \'%a\' "$vars_file" >"$RA8_TEST_MODE"\n'
719 '[ "$ANSIBLE_CONFIG" = "$PWD/ansible.cfg" ]\n'
720 '[ "$ANSIBLE_COLLECTIONS_PATH" = "$PWD/../../.ansible/collections" ]\n'
721 '[ "$ANSIBLE_COLLECTIONS_SCAN_SYS_PATH" = false ]\n'
722 '[ "$PYTHONNOUSERSITE" = 1 ]\n'
723 '[ -z "${PYTHONHOME:-}" ]\n[ -z "${PYTHONPATH:-}" ]\n'
724 '[ -z "${ANSIBLE_ROLES_PATH:-}" ]\n'
725 f'grep -q {shlex.quote(sentinel)} "$vars_file"\n'
726 f"exit {FAKE_ANSIBLE_FAILURE_STATUS}\n",
727 )
728
729
730def _make_fixture(root: Path, sentinel: str) -> _SelftestFixture:
731 """Create a fake failing Ansible executable and its observation paths."""
732 stage = root / "stage with spaces"
733 _write_stage_authorities(stage)
734 managed_root = root / "managed tools"
735 managed_cache = root / "managed cache" / "uv"
736 managed_cache.mkdir(parents=True)
737 system_bin = root / "system"
738 system_bin.mkdir()
739 fixture = _SelftestFixture(
740 stage,
741 managed_root,
742 managed_cache,
743 system_bin / "python3",
744 root / "args.log",
745 root / "vars-path.log",
746 root / "mode.log",
747 root / "uv.log",
748 root / "python.log",
749 root / "SHOULD_NOT_EXIST",
750 root / "HOSTILE_PATH_RAN",
751 )
752 _write_fake_toolchain(root, fixture)
753 _write_fake_playbook(fixture, sentinel)
754 hostile_bin = root / "hostile"
755 hostile_bin.mkdir()
756 _write_executable(
757 hostile_bin / "ansible-playbook",
758 f"#!/bin/sh\ntouch {shlex.quote(str(fixture.hostile_executed))}\nexit 99\n",
759 )
760 return fixture
761
762
763def _run_script(
764 script: str,
765 fixture: _SelftestFixture,
766 uv_failure: str = "",
767 expected_system_python: str = "",
768 verifier_status: int = 0,
769) -> subprocess.CompletedProcess[str]:
770 """Run the offline WSL shell with only a fake Ansible executable."""
771 env = {
772 **os.environ,
773 "PATH": f"{fixture.stage.parent / 'hostile'}:/usr/bin:/bin",
774 "RA8_TEST_ARGS": str(fixture.args_log),
775 "RA8_TEST_SYSTEM_PYTHON": expected_system_python or str(fixture.system_python),
776 "RA8_TEST_VARS_PATH": str(fixture.vars_path_log),
777 "RA8_TEST_MODE": str(fixture.mode_log),
778 "RA8_TEST_UV": str(fixture.stage.parent / "verified-uv"),
779 "RA8_TEST_UV_LOG": str(fixture.uv_log),
780 "RA8_TEST_UV_FAIL": uv_failure,
781 "RA8_TEST_VERIFY_STATUS": str(verifier_status),
782 "RA8_TEST_STAGE": str(fixture.stage),
783 "RA8_TEST_PYTHON_LOG": str(fixture.python_log),
784 "ANSIBLE_CONFIG": str(fixture.stage.parent / "hostile.cfg"),
785 "ANSIBLE_ROLES_PATH": str(fixture.stage.parent / "hostile-roles"),
786 "PYTHONHOME": str(fixture.stage.parent / "hostile-python-home"),
787 "PYTHONPATH": str(fixture.stage.parent / "hostile-python-path"),
788 "UV_CONFIG_FILE": str(fixture.stage.parent / "hostile-uv.toml"),
789 "UV_INDEX_URL": "https://hostile.invalid/simple",
790 }
791 return subprocess.run(
792 ["/bin/bash"], input=script, text=True, env=env, capture_output=True, check=False
793 )
794
795
796def _check_result(
797 result: subprocess.CompletedProcess[str],
798 fixture: _SelftestFixture,
799 attack: str,
800 sentinel: str,
801 expected_mode: str = "600",
802) -> list[str]:
803 """Check argv integrity, mode, redaction, and cleanup after fake failure."""
804 if result.returncode != FAKE_ANSIBLE_FAILURE_STATUS:
805 message = (
806 f"fake failing Ansible returned {result.returncode}, "
807 f"expected {FAKE_ANSIBLE_FAILURE_STATUS}"
808 )
809 return [message]
810 failures: list[str] = []
811 argv_text = fixture.args_log.read_text(encoding="utf-8")
812 if attack not in argv_text.splitlines() or fixture.injected_path.exists():
813 failures.append("WSL argument quoting did not preserve a metacharacter-bearing tag")
814 if sentinel in argv_text:
815 failures.append("WSL secret appeared in ansible-playbook argv")
816 if '"fleet_capacity_state_group": "root"' not in argv_text:
817 failures.append("WSL capacity state group did not bind to its root executor")
818 remote_vars = Path(fixture.vars_path_log.read_text(encoding="utf-8").strip())
819 if fixture.mode_log.read_text(encoding="utf-8").strip() != expected_mode:
820 failures.append(f"WSL temporary vars transport was not mode 0{expected_mode}")
821 if remote_vars.exists():
822 failures.append("WSL temporary vars transport survived Ansible failure")
823 if fixture.hostile_executed.exists():
824 failures.append("WSL used a hostile PATH ansible-playbook")
825 return failures
826
827
828def _render_fixture(
829 data: dict[str, Any], fixture: _SelftestFixture, typed: ftv.TypedVars, attack: str, mode: str
830) -> tuple[str, list[str]]:
831 """Render one offline WSL converge against only fixture-owned paths."""
832 spec = ConvergeSpec(
833 data,
834 "win-ci",
835 ["wsl-ci-host"],
836 ["--tags", attack],
837 typed,
838 mode,
839 str(fixture.stage),
840 str(fixture.managed_root / "bin" / "ansible-playbook"),
841 str(fixture.system_python),
842 str(fixture.managed_root),
843 str(fixture.managed_cache),
844 )
845 return render_converge(spec)
846
847
848def _environment_drift_selftest(check_script: str, fixture: _SelftestFixture) -> list[str]:
849 """Prove only safely authenticated environment drift requests apply."""
850 failures: list[str] = []
851 sync_drift = _run_script(check_script, fixture, "sync-drift")
852 if sync_drift.returncode != fws.REMOTE_APPLY_REQUIRED_STATUS:
853 failures.append("safe WSL sync drift was not classified apply-required")
854 package_drift = _run_script(check_script, fixture, verifier_status=1)
855 if package_drift.returncode != fws.REMOTE_APPLY_REQUIRED_STATUS:
856 failures.append("safe WSL package drift was not classified apply-required")
857 verifier_failure = _run_script(check_script, fixture, verifier_status=FATAL_VERIFIER_STATUS)
858 if verifier_failure.returncode != FATAL_VERIFIER_STATUS:
859 failures.append("fatal WSL package authentication failure was classified as drift")
860 marker = fixture.managed_root / ".ra8-infra-lock.sha256"
861 marker.chmod(0o666)
862 unsafe = _run_script(check_script, fixture)
863 marker.chmod(0o644)
864 if unsafe.returncode != 1:
865 failures.append("unsafe WSL environment marker was classified as drift")
866 return failures
867
868
869def _toolchain_mode_selftest(
870 data: dict[str, Any], fixture: _SelftestFixture, typed: ftv.TypedVars, attack: str
871) -> tuple[list[str], str, list[str], str]:
872 """Exercise apply sync, check-only verification, and uv failure handling."""
873 failures: list[str] = []
874 apply_script, summaries = _render_fixture(data, fixture, typed, attack, "apply")
875 apply_result = _run_script(apply_script, fixture)
876 failures.extend(_check_result(apply_result, fixture, attack, "fleet-secret-sentinel"))
877 marker = fixture.managed_root / ".ra8-infra-lock.sha256"
878 if not marker.is_file() or marker.is_symlink():
879 failures.append("WSL apply did not publish its exact lock marker")
880 check_script, _ = _render_fixture(data, fixture, typed, attack, "check")
881 check_result = _run_script(check_script, fixture)
882 if list(fixture.managed_root.glob(".ra8-infra-export.*")):
883 failures.append("WSL check left a durable lock-export file")
884 if ".ra8-infra-export." in check_script or '>"$locked_export"' in check_script:
885 failures.append("WSL check still renders a durable lock-export write")
886 failures.extend(
887 _check_result(
888 check_result,
889 fixture,
890 attack,
891 "fleet-secret-sentinel",
892 expected_mode="500",
893 )
894 )
895 if fixture.uv_log.is_file():
896 sync_calls = [
897 line
898 for line in fixture.uv_log.read_text(encoding="ascii").splitlines()
899 if "sync" in line
900 ]
901 expected_sync_calls = 2
902 if (
903 len(sync_calls) != expected_sync_calls
904 or "--check" in sync_calls[0]
905 or "--check" not in sync_calls[1]
906 ):
907 failures.append(
908 "WSL apply/check modes did not preserve sync versus verify-only behavior"
909 )
910 else:
911 failures.append("WSL apply/check did not invoke the authenticated uv fixture")
912 hardcoded_python = _run_script(
913 apply_script,
914 fixture,
915 expected_system_python="/usr/bin/python3",
916 )
917 if hardcoded_python.returncode != FAKE_UV_ARGV_FAILURE_STATUS:
918 failures.append(
919 "WSL did not reject a hardcoded system Python path: "
920 f"expected {FAKE_UV_ARGV_FAILURE_STATUS}, got "
921 f"{hardcoded_python.returncode}"
922 )
923 sync_failure = _run_script(apply_script, fixture, "sync")
924 if sync_failure.returncode != FAKE_UV_FAILURE_STATUS:
925 failures.append("WSL masked an authenticated uv sync failure")
926 export_failure = _run_script(check_script, fixture, "export")
927 if export_failure.returncode != FAKE_UV_FAILURE_STATUS:
928 failures.append("WSL masked an authenticated uv export failure")
929 return failures, apply_script, summaries, check_script
930
931
932def _toolchain_authority_selftest(
933 fixture: _SelftestFixture,
934 mode_result: tuple[list[str], str, list[str], str],
935) -> tuple[list[str], str, list[str]]:
936 """Exercise bootstrap-helper identity and staged-config link rejection."""
937 failures, apply_script, summaries, check_script = mode_result
938 failures.extend(_environment_drift_selftest(check_script, fixture))
939
940 helper = fixture.stage / "scripts/dev/bootstrap_uv_exec.py"
941 helper.chmod(0o755)
942 wrong_mode = _run_script(check_script, fixture)
943 if wrong_mode.returncode == FAKE_ANSIBLE_FAILURE_STATUS:
944 failures.append("WSL accepted a wrong-mode bootstrap execution helper")
945 helper.chmod(0o644)
946 original_helper = helper.read_bytes()
947 helper.write_bytes(original_helper + b"changed\n")
948 changed_helper = _run_script(check_script, fixture)
949 if changed_helper.returncode == FAKE_ANSIBLE_FAILURE_STATUS:
950 failures.append("WSL accepted changed bootstrap execution-helper bytes")
951 helper.write_bytes(original_helper)
952 helper.chmod(0o644)
953
954 config = fixture.stage / "infra" / "ansible" / "ansible.cfg"
955 real_config = config.with_name("real.cfg")
956 config.rename(real_config)
957 config.symlink_to(real_config)
958 linked_result = _run_script(check_script, fixture)
959 if linked_result.returncode == FAKE_ANSIBLE_FAILURE_STATUS:
960 failures.append("WSL accepted a symlinked staged Ansible config")
961 return failures, apply_script, summaries
962
963
964def _toolchain_selftest(
965 data: dict[str, Any], fixture: _SelftestFixture, typed: ftv.TypedVars, attack: str
966) -> tuple[list[str], str, list[str]]:
967 """Exercise managed-tool modes and authority rejection in original order."""
968 mode_result = _toolchain_mode_selftest(data, fixture, typed, attack)
969 return _toolchain_authority_selftest(fixture, mode_result)
970
971
972def run_selftest(data: dict[str, Any]) -> list[str]:
973 """Exercise quoted WSL transport, redaction, and failure cleanup."""
974 sentinel = "fleet-secret-sentinel"
975 content = f"ci_runner_docker_registration_token: {sentinel}\n".encode()
976 typed = ftv.TypedVars(Path("/captured/registration.yml"), content)
977 stage_failures = fws.run_selftest()
978 with tempfile.TemporaryDirectory(prefix="ra8-fleet-wsl-") as scratch:
979 fixture = _make_fixture(Path(scratch), sentinel)
980 attack = f"capacity; touch {fixture.injected_path}"
981 failures, script, summaries = _toolchain_selftest(data, fixture, typed, attack)
982 if not fixture.uv_log.is_file():
983 failures.append("WSL managed-tool mode selftest did not execute")
984 config = fixture.stage / "infra" / "ansible" / "ansible.cfg"
985 if not config.is_symlink():
986 failures.append("WSL managed-authority selftest did not execute")
987 failures.extend(_remote_boundary_selftest(data, Path(scratch)))
988 failures[:0] = stage_failures
989 encoded = base64.b64encode(content).decode("ascii")
990 summary = "\n".join(summaries)
991 if sentinel in script or sentinel in summary or encoded in summary:
992 failures.append("WSL secret appeared in raw script text or rendered summaries")
993 return failures