Wait for installer exit and live shell before followup probes

This commit is contained in:
Codex
2026-10-13 05:39:59 +03:00
parent 32e9334feb
commit 17ade497a4
2 changed files with 42 additions and 0 deletions
+12
View File
@@ -505,6 +505,17 @@ class Console:
re.escape(token.encode()) + rb':INSTALL_EXIT:[0-9]+\x1f')
return self.expect(pattern, timeout, failure_pattern=failure)
def finish_installer(self, token, timeout=30):
# Completion text can arrive before the process exits and readline
# resumes. Input sent during that gap can be discarded by the TTY.
deadline = time.monotonic() + timeout
returned = self.expect(rb'\x1e' + re.escape(token.encode()) +
rb':INSTALL_EXIT:([0-9]+)\x1f', timeout)
status = int(returned.group(1))
if status:
raise SmokeError(f'Installer returned status {status} after completion')
self.expect(rb'hydra-live[^\r\n]*[#$] ', max(0, deadline - time.monotonic()))
def command(self, command, timeout=60):
token = secrets.token_hex(12)
self.buffer = b''
@@ -818,6 +829,7 @@ def main(argv=None):
console.send(password + '\n')
console.expect_installer(rb'Installation completed\. Diagnostic log: [^\r\n]+',
args.install_timeout, installer_token)
console.finish_installer(installer_token)
checkpoint('real_installer', b'Installer completed via guest controlling TTY')
console.command("sudo -n mkdir -p /mnt/hydra-validation && sudo -n mount /dev/vda2 /mnt/hydra-validation && printf %s " +
shlex.quote(sentinel) + " | sudo -n tee /mnt/hydra-validation/var/lib/hydra-acceptance-sentinel >/dev/null && sudo -n umount /mnt/hydra-validation", 60)
+30
View File
@@ -2,6 +2,7 @@
import importlib.util
from pathlib import Path
import socket
import select
import subprocess
import tempfile
import threading
@@ -152,6 +153,35 @@ class SmokeTests(unittest.TestCase):
console.expect_installer(rb'Repeat password: ', 0.05, 'testtoken')
self.assertNotIn('test-secret', str(caught.exception))
def test_installer_followup_waits_for_exit_and_new_shell_prompt(self):
console, guest = self.installer_console()
premature = []
def respond():
guest.sendall(b'Installation completed. Diagnostic log: /run/installer.log\r\n')
premature.append(bool(select.select([guest], [], [], 0.05)[0]))
guest.sendall(b'\x1etesttoken:INSTALL_EXIT:0\x1f')
premature.append(bool(select.select([guest], [], [], 0.05)[0]))
guest.sendall(b'\x1b[?2004hhydra-live@hydra-os ~ $ ')
data = b''
while not data.endswith(b'\n'):
data += guest.recv(65536)
result = subprocess.run(['/bin/sh', '-c', data.decode()], capture_output=True)
guest.sendall(result.stdout)
worker = threading.Thread(target=respond, daemon=True)
worker.start()
console.expect_installer(rb'Installation completed\. Diagnostic log: [^\r\n]+',
1, 'testtoken')
console.finish_installer('testtoken', timeout=1)
self.assertEqual(console.command('printf ready', timeout=1), b'ready')
worker.join(timeout=1)
self.assertEqual(premature, [False, False], 'command reached installer before shell was ready')
def test_installer_completion_does_not_override_nonzero_return(self):
console, guest = self.installer_console()
guest.sendall(b'\x1etesttoken:INSTALL_EXIT:1\x1fhydra-live@hydra-os ~ $ ')
with self.assertRaisesRegex(self.mod.SmokeError, 'Installer.*status 1'):
console.finish_installer('testtoken', timeout=0.05)
def test_frame_real_shell_roundtrip_does_not_accept_sentinel_in_echo(self):
one, two = socket.socketpair()
self.addCleanup(one.close)