diff --git a/qubesadmin/tests/tools/qvm_template.py b/qubesadmin/tests/tools/qvm_template.py index 8314629e7..e6ee7f778 100644 --- a/qubesadmin/tests/tools/qvm_template.py +++ b/qubesadmin/tests/tools/qvm_template.py @@ -196,7 +196,8 @@ def test_004_verify_rpm_badname(self, mock_proc, mock_call, mock_ts): @mock.patch('os.path.exists') @mock.patch('subprocess.Popen') - def test_010_extract_rpm_success(self, mock_popen, mock_path_exists): + @mock.patch('os.symlink') + def test_010_extract_rpm_success(self, mock_symlink, mock_popen, mock_path_exists): mock_popen.return_value.__enter__.return_value = mock_popen.return_value pipe = mock.Mock() mock_popen.return_value.stdout = pipe @@ -234,20 +235,17 @@ def test_010_extract_rpm_success(self, mock_popen, mock_path_exists): ]), mock.call().__enter__(), mock.call().__exit__(None, None, None), - mock.call([ - 'ln', - '-s', - path, - dirpath + '//var/lib/qubes/vm-templates/test-vm/template.rpm' - ]), - mock.call().__enter__(), - mock.call().__exit__(None, None, None), + ]) + self.assertEqual(mock_symlink.mock_calls, [ + mock.call(path, + dirpath + '//var/lib/qubes/vm-templates/test-vm/template.rpm') ]) self.assertAllCalled() @mock.patch('os.path.exists') @mock.patch('subprocess.Popen') - def test_011_extract_rpm_fail(self, mock_popen, mock_path_exists): + @mock.patch('os.symlink') + def test_011_extract_rpm_fail(self, mock_symlink, mock_popen, mock_path_exists): for failing_call in range(1, 5): mock_popen.reset_mock() with self.subTest(failing_call=failing_call): @@ -269,6 +267,12 @@ def side_effect(_, **__): mock_popen.side_effect = side_effect mock_path_exists.return_value = True + def symlink_side_effect(_s, _d): + if failing_call >= 4: + raise OSError("Error") + return None + mock_symlink.side_effect = symlink_side_effect + with tempfile.NamedTemporaryFile() as fd, \ tempfile.TemporaryDirectory() as tmpdir: path = fd.name @@ -302,16 +306,6 @@ def side_effect(_, **__): ]), mock.call().__enter__(), mock.call().__exit__(None, None, None), - ]) + ([] if failing_call < 4 else [ - mock.call([ - 'ln', - '-s', - path, - dirpath - + '//var/lib/qubes/vm-templates/test-vm/template.rpm' - ]), - mock.call().__enter__(), - mock.call().__exit__(None, None, None), ])) self.assertAllCalled() diff --git a/qubesadmin/tools/qvm_template.py b/qubesadmin/tools/qvm_template.py index cd39c8879..bdd447648 100644 --- a/qubesadmin/tools/qvm_template.py +++ b/qubesadmin/tools/qvm_template.py @@ -768,11 +768,13 @@ def extract_rpm(name: str, path: str, target: str) -> bool: if truncate.returncode != 0: return False # and create rpm file symlink - with subprocess.Popen([ - 'ln', '-s', path, f'{target}/{PATH_PREFIX}/{name}/template.rpm' - ]) as symlink: - pass - if symlink.returncode != 0: + link_path = f'{target}/{PATH_PREFIX}/{name}/template.rpm' + try: + os.symlink(os.path.abspath(path), + f'{target}/{PATH_PREFIX}/{name}/template.rpm') + except OSError as e: + print(f"Failed to create {link_path} symlink: {e!s}", + file=sys.stderr) return False return True