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()}
|
return {str(p.relative_to(path)): sha(p) for p in sorted(path.rglob('*')) if p.is_file()}
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def mounted(device, options=''):
|
def mounted(device, filesystem, options=''):
|
||||||
import tempfile
|
import tempfile
|
||||||
with tempfile.TemporaryDirectory(prefix='hydra-acceptance-') as directory:
|
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)
|
run(*args)
|
||||||
try: yield Path(directory)
|
try: yield Path(directory)
|
||||||
finally: run('umount', directory)
|
finally: run('umount', directory)
|
||||||
@@ -149,7 +149,7 @@ def mounted(device, options=''):
|
|||||||
def preserved():
|
def preserved():
|
||||||
table = json.loads(run('sfdisk', '--json', '/dev/vda'))['partitiontable']
|
table = json.loads(run('sfdisk', '--json', '/dev/vda'))['partitiontable']
|
||||||
# No filesystem UUIDs: selected root UUID must change on formatting.
|
# 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/')}
|
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)
|
order = re.search(r'^BootOrder:\s*(.*)$', run('efibootmgr'), re.M)
|
||||||
assert order, 'UEFI BootOrder was not observable'
|
assert order, 'UEFI BootOrder was not observable'
|
||||||
@@ -213,7 +213,7 @@ run('udevadm', 'settle')
|
|||||||
run('mkfs.vfat', '-F', '32', '/dev/vda1')
|
run('mkfs.vfat', '-F', '32', '/dev/vda1')
|
||||||
with open('/dev/vda2', 'r+b', buffering=0) as foreign:
|
with open('/dev/vda2', 'r+b', buffering=0) as foreign:
|
||||||
foreign.write((context['sentinel'] * 1024).encode()); os.fsync(foreign.fileno())
|
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'):
|
for name in ('EFI/ForeignOS/loader.efi', 'EFI/BOOT/BOOTX64.EFI'):
|
||||||
path = esp / name; path.parent.mkdir(parents=True, exist_ok=True)
|
path = esp / name; path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
path.write_text(context['sentinel'] + ':' + name)
|
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()}
|
result = {'fixture_emerge': output, 'home': Path('/home/hydratest/acceptance-home').read_text()}
|
||||||
''',
|
''',
|
||||||
'base-state': r'''
|
'base-state': r'''
|
||||||
with mounted('/dev/vda3', 'ro,subvol=@root') as root:
|
with mounted('/dev/vda3', 'btrfs', 'ro,subvol=@root') as root:
|
||||||
with mounted('/dev/vda1', 'ro') as esp:
|
with mounted('/dev/vda1', 'vfat', 'ro') as esp:
|
||||||
result = base_state(root, esp)
|
result = base_state(root, esp)
|
||||||
''',
|
''',
|
||||||
'damage': r'''
|
'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*'))
|
kernels = list((root / 'boot').glob('vmlinuz*'))
|
||||||
assert kernels, 'No actual installed kernel to damage'
|
assert kernels, 'No actual installed kernel to damage'
|
||||||
for kernel in kernels: kernel.write_bytes(b'DELIBERATE ACCEPTANCE KERNEL DAMAGE\n')
|
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'
|
loader = esp / 'EFI/HydraOS/grubx64.efi'
|
||||||
assert loader.is_file(); loader.write_bytes(b'DELIBERATE ACCEPTANCE EFI DAMAGE\n')
|
assert loader.is_file(); loader.write_bytes(b'DELIBERATE ACCEPTANCE EFI DAMAGE\n')
|
||||||
run('sync')
|
run('sync')
|
||||||
@@ -263,7 +263,7 @@ assert marker['root_subvolume'] == '@root'
|
|||||||
assert Path('/home/hydratest/acceptance-home').read_text() == 'current-' + context['sentinel']
|
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 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'
|
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['home'] = Path('/home/hydratest/acceptance-home').read_text()
|
||||||
result['vm_data'] = tree(Path('/home/hydratest/.local/share/hydra/acceptance-guest'))
|
result['vm_data'] = tree(Path('/home/hydratest/.local/share/hydra/acceptance-guest'))
|
||||||
result['root_mount'] = run('findmnt', '-n', '-o', 'SOURCE,OPTIONS', '/')
|
result['root_mount'] = run('findmnt', '-n', '-o', 'SOURCE,OPTIONS', '/')
|
||||||
@@ -339,7 +339,7 @@ recovery.RecoverySession.save_journal = pause
|
|||||||
raise SystemExit(recovery.main(ARGUMENTS))
|
raise SystemExit(recovery.main(ARGUMENTS))
|
||||||
""".replace('ARGUMENTS', repr(['restore', '--root-uuid', context['root_uuid'], '--checkpoint', context['checkpoint'], '--execute', '--confirm-root-uuid', context['root_uuid']]))
|
""".replace('ARGUMENTS', repr(['restore', '--root-uuid', context['root_uuid'], '--checkpoint', context['checkpoint'], '--execute', '--confirm-root-uuid', context['root_uuid']]))
|
||||||
stop_child(source)
|
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())
|
journal = json.loads((top / '@snapshots/restore.json').read_text())
|
||||||
assert journal['phase'] == 'root-retained'
|
assert journal['phase'] == 'root-retained'
|
||||||
assert not (top / '@root').exists() and (top / journal['failed_root']).exists()
|
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.assertNotIn('--offline', constants, 'emerge has no --offline option')
|
||||||
self.assertIn('--ignore-default-opts', constants)
|
self.assertIn('--ignore-default-opts', constants)
|
||||||
self.assertIn('--nodeps', 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