Skip to content

Commit

Permalink
Fix creating template.rpm symlink
Browse files Browse the repository at this point in the history
Create symlink using absolute path to the template rpm package.
This is important because relative path given to qvm-template should be
considered in relation to the current working directory, not
template.rpm symlink location.
This fixes calling `qvm-template install` using relative path to the
package.

Fixes QubesOS/qubes-issues#9246
  • Loading branch information
marmarek committed May 19, 2024
1 parent a0c61ee commit 7f8da8a
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 25 deletions.
34 changes: 14 additions & 20 deletions qubesadmin/tests/tools/qvm_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -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()

Expand Down
12 changes: 7 additions & 5 deletions qubesadmin/tools/qvm_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 7f8da8a

Please sign in to comment.