fix(smoke): explicitly select known guest filesystems when mounting
This commit is contained in:
+10
-10
@@ -138,10 +138,10 @@ def tree(path):
|
||||
return {str(p.relative_to(path)): sha(p) for p in sorted(path.rglob('*')) if p.is_file()}
|
||||
|
||||
@contextlib.contextmanager
|
||||
def mounted(device, options=''):
|
||||
def mounted(device, filesystem, options=''):
|
||||
import tempfile
|
||||
with tempfile.TemporaryDirectory(prefix='hydra-acceptance-') as directory:
|
||||
args = ['mount'] + (['-o', options] if options else []) + [device, directory]
|
||||
args = ['mount', '-t', filesystem] + (['-o', options] if options else []) + [device, directory]
|
||||
run(*args)
|
||||
try: yield Path(directory)
|
||||
finally: run('umount', directory)
|
||||
@@ -149,7 +149,7 @@ def mounted(device, options=''):
|
||||
def preserved():
|
||||
table = json.loads(run('sfdisk', '--json', '/dev/vda'))['partitiontable']
|
||||
# No filesystem UUIDs: selected root UUID must change on formatting.
|
||||
with mounted('/dev/vda1', 'ro') as esp:
|
||||
with mounted('/dev/vda1', 'vfat', 'ro') as esp:
|
||||
foreign = {k: v for k, v in tree(esp / 'EFI').items() if not k.startswith('HydraOS/')}
|
||||
order = re.search(r'^BootOrder:\s*(.*)$', run('efibootmgr'), re.M)
|
||||
assert order, 'UEFI BootOrder was not observable'
|
||||
@@ -213,7 +213,7 @@ run('udevadm', 'settle')
|
||||
run('mkfs.vfat', '-F', '32', '/dev/vda1')
|
||||
with open('/dev/vda2', 'r+b', buffering=0) as foreign:
|
||||
foreign.write((context['sentinel'] * 1024).encode()); os.fsync(foreign.fileno())
|
||||
with mounted('/dev/vda1') as esp:
|
||||
with mounted('/dev/vda1', 'vfat') as esp:
|
||||
for name in ('EFI/ForeignOS/loader.efi', 'EFI/BOOT/BOOTX64.EFI'):
|
||||
path = esp / name; path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(context['sentinel'] + ':' + name)
|
||||
@@ -242,16 +242,16 @@ with Path('/var/lib/portage/world').open('a') as world: world.write('# acceptanc
|
||||
result = {'fixture_emerge': output, 'home': Path('/home/hydratest/acceptance-home').read_text()}
|
||||
''',
|
||||
'base-state': r'''
|
||||
with mounted('/dev/vda3', 'ro,subvol=@root') as root:
|
||||
with mounted('/dev/vda1', 'ro') as esp:
|
||||
with mounted('/dev/vda3', 'btrfs', 'ro,subvol=@root') as root:
|
||||
with mounted('/dev/vda1', 'vfat', 'ro') as esp:
|
||||
result = base_state(root, esp)
|
||||
''',
|
||||
'damage': r'''
|
||||
with mounted('/dev/vda3', 'subvol=@root') as root:
|
||||
with mounted('/dev/vda3', 'btrfs', 'subvol=@root') as root:
|
||||
kernels = list((root / 'boot').glob('vmlinuz*'))
|
||||
assert kernels, 'No actual installed kernel to damage'
|
||||
for kernel in kernels: kernel.write_bytes(b'DELIBERATE ACCEPTANCE KERNEL DAMAGE\n')
|
||||
with mounted('/dev/vda1') as esp:
|
||||
with mounted('/dev/vda1', 'vfat') as esp:
|
||||
loader = esp / 'EFI/HydraOS/grubx64.efi'
|
||||
assert loader.is_file(); loader.write_bytes(b'DELIBERATE ACCEPTANCE EFI DAMAGE\n')
|
||||
run('sync')
|
||||
@@ -263,7 +263,7 @@ assert marker['root_subvolume'] == '@root'
|
||||
assert Path('/home/hydratest/acceptance-home').read_text() == 'current-' + context['sentinel']
|
||||
assert not Path('/var/db/pkg/app-misc/hydra-acceptance-2.0').exists()
|
||||
assert Path('/usr/share/hydra/base-fixture.txt').read_text().strip() == '1.0'
|
||||
with mounted('/dev/vda1', 'ro') as esp: result = base_state(Path('/'), esp)
|
||||
with mounted('/dev/vda1', 'vfat', 'ro') as esp: result = base_state(Path('/'), esp)
|
||||
result['home'] = Path('/home/hydratest/acceptance-home').read_text()
|
||||
result['vm_data'] = tree(Path('/home/hydratest/.local/share/hydra/acceptance-guest'))
|
||||
result['root_mount'] = run('findmnt', '-n', '-o', 'SOURCE,OPTIONS', '/')
|
||||
@@ -339,7 +339,7 @@ recovery.RecoverySession.save_journal = pause
|
||||
raise SystemExit(recovery.main(ARGUMENTS))
|
||||
""".replace('ARGUMENTS', repr(['restore', '--root-uuid', context['root_uuid'], '--checkpoint', context['checkpoint'], '--execute', '--confirm-root-uuid', context['root_uuid']]))
|
||||
stop_child(source)
|
||||
with mounted('/dev/vda3', 'ro,subvolid=5') as top:
|
||||
with mounted('/dev/vda3', 'btrfs', 'ro,subvolid=5') as top:
|
||||
journal = json.loads((top / '@snapshots/restore.json').read_text())
|
||||
assert journal['phase'] == 'root-retained'
|
||||
assert not (top / '@root').exists() and (top / journal['failed_root']).exists()
|
||||
|
||||
@@ -117,3 +117,27 @@ class LaptopSmokeTests(unittest.TestCase):
|
||||
self.assertNotIn('--offline', constants, 'emerge has no --offline option')
|
||||
self.assertIn('--ignore-default-opts', constants)
|
||||
self.assertIn('--nodeps', constants)
|
||||
|
||||
def test_guest_mount_explicitly_selects_known_filesystem(self):
|
||||
namespace = {}
|
||||
exec(self.mod.GUEST_COMMON, namespace)
|
||||
calls = []
|
||||
namespace['run'] = lambda *args: calls.append(args)
|
||||
for device, filesystem, options in (('/dev/vda1', 'vfat', 'ro'),
|
||||
('/dev/vda2', 'ext4', 'ro'),
|
||||
('/dev/vda3', 'btrfs', 'ro,subvol=@root')):
|
||||
with self.subTest(device=device):
|
||||
with namespace['mounted'](device, filesystem, options) as directory:
|
||||
self.assertEqual(calls[-1], ('mount', '-t', filesystem, '-o', options, device, str(directory)))
|
||||
self.assertEqual(calls[-1], ('umount', str(directory)))
|
||||
|
||||
def test_every_guest_mount_call_names_the_partition_filesystem(self):
|
||||
import ast
|
||||
expected = {'/dev/vda1': 'vfat', '/dev/vda2': 'ext4', '/dev/vda3': 'btrfs'}
|
||||
sources = [self.mod.GUEST_COMMON, *self.mod.GUEST_BODIES.values()]
|
||||
for source in sources:
|
||||
for node in ast.walk(ast.parse(source)):
|
||||
if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'mounted':
|
||||
with self.subTest(call=ast.unparse(node)):
|
||||
self.assertGreaterEqual(len(node.args), 2)
|
||||
self.assertEqual(ast.literal_eval(node.args[1]), expected[ast.literal_eval(node.args[0])])
|
||||
|
||||
Reference in New Issue
Block a user