856 lines
37 KiB
Python
856 lines
37 KiB
Python
from __future__ import absolute_import
|
|
|
|
import os
|
|
from os import path, makedirs
|
|
import tempfile
|
|
import unittest
|
|
try:
|
|
from unittest import mock
|
|
from unittest.mock import patch, MagicMock, Mock, call
|
|
except ImportError:
|
|
import mock
|
|
from mock import patch, MagicMock, Mock, call
|
|
|
|
import requests_mock
|
|
import shutil
|
|
import six
|
|
from six.moves import range
|
|
|
|
import koji
|
|
from koji.tasks import BaseTaskHandler, FakeTask, ForkTask, SleepTask, WaitTestTask, scan_mounts, \
|
|
umount_all, safe_rmtree
|
|
|
|
|
|
def get_fake_mounts_file():
|
|
""" Returns contents of /prc/mounts in a file-like object
|
|
"""
|
|
return six.StringIO(six.text_type((
|
|
'sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0\n'
|
|
'proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\n'
|
|
'devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=238836k,nr_inodes=59709,mode=755 0 0\n'
|
|
'securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0\n'
|
|
'tmpfs /dev/shm\\040(deleted) tmpfs rw,seclabel,nosuid,nodev 0 0\n'
|
|
'devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0\n'
|
|
'tmpfs /run tmpfs rw,seclabel,nosuid,nodev,mode=755 0 0\n'
|
|
'tmpfs /sys/fs/cgroup tmpfs ro,seclabel,nosuid,nodev,noexec,mode=755 0 0\n'
|
|
'pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0\n'
|
|
'cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0\n'
|
|
'cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0\n'
|
|
'cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0\n'
|
|
'cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0\n'
|
|
'cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0\n'
|
|
'cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0\n'
|
|
'cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0\n'
|
|
'cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0\n'
|
|
'cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0\n'
|
|
'configfs /sys/kernel/config configfs rw,relatime 0 0\n'
|
|
'hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime 0 0\n'
|
|
'mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0\n'
|
|
)))
|
|
|
|
|
|
class TaskTest(BaseTaskHandler):
|
|
Methods = ['some_method']
|
|
_taskWeight = 5.2
|
|
|
|
def handler(self, *args):
|
|
return 42
|
|
|
|
|
|
class TaskNoWeightTest(BaseTaskHandler):
|
|
Methods = ['some_method']
|
|
|
|
def handler(self, *args):
|
|
return 42
|
|
|
|
|
|
class BadTask(BaseTaskHandler):
|
|
Methods = ['some_method']
|
|
|
|
|
|
class TasksTestCase(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.tempdir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.tempdir)
|
|
|
|
def get_tmp_dir_path(self, name):
|
|
return os.path.join(self.tempdir, name)
|
|
|
|
def test_scan_mounts_results(self):
|
|
""" Tests the scan_mounts function with a mocked /proc/mounts file.
|
|
A list containing mount points starting with /dev are expected to be returned
|
|
from the function based on the function input of /dev.
|
|
"""
|
|
fake_mounts_file_contents = get_fake_mounts_file()
|
|
|
|
with patch('koji.tasks.open', return_value=fake_mounts_file_contents, create=True):
|
|
self.assertIn(scan_mounts('/dev'),
|
|
[['/dev/shm', '/dev/pts', '/dev/mqueue', '/dev/hugepages', '/dev']])
|
|
|
|
def test_scan_mounts_no_results(self):
|
|
""" Tests the scan_mounts function with a mocked /proc/mounts file.
|
|
An argument of /nonexistent/path to the function should return an empty list.
|
|
"""
|
|
fake_mounts_file_contents = get_fake_mounts_file()
|
|
|
|
with patch('koji.tasks.open', return_value=fake_mounts_file_contents, create=True):
|
|
self.assertEqual(scan_mounts('/nonexistent/path'), [])
|
|
|
|
# Patching the scan_mounts function instead of the built-in open function
|
|
# because this is only testing umount_all
|
|
@patch('koji.tasks.scan_mounts', side_effect=[['/dev/shm', '/dev/pts', '/dev/mqueue'], []])
|
|
@patch('os.spawnvp', return_value=0)
|
|
def test_umount_all(self, mocked_spawnvp, mocked_scan_mounts):
|
|
""" Tests that umount_all returns nothing when successful.
|
|
"""
|
|
self.assertEqual(umount_all('/test'), None)
|
|
|
|
# Patching the scan_mounts function instead of the built-in open function
|
|
# because this is only testing umount_all
|
|
@patch('koji.tasks.scan_mounts', return_value=['/dev/shm', '/dev/pts', '/dev/mqueue'])
|
|
@patch('os.spawnvp', return_value=1)
|
|
def test_umount_all_failure(self, mocked_spawnvp, mocked_scan_mounts):
|
|
""" Tests that umount_all raises an exception when a mount point can't be unmounted.
|
|
"""
|
|
try:
|
|
umount_all('/dev')
|
|
raise Exception('A GenericError was not raised during the test')
|
|
except koji.GenericError as e:
|
|
self.assertEqual(e.args[0], 'umount failed (exit code 1) for /dev/shm')
|
|
|
|
# Patching the scan_mounts function instead of the built-in open function
|
|
# because this is only testing umount_all
|
|
@patch('koji.tasks.scan_mounts',
|
|
side_effect=[['/dev/shm', '/dev/pts', '/dev/mqueue'], ['/dev/shm', '/dev/mqueue']])
|
|
@patch('os.spawnvp', return_value=0)
|
|
def test_umount_all_unexpected_failure(self, mocked_spawnvp, mocked_scan_mounts):
|
|
""" Tests that umount_all will fail if the command to unmount the mount
|
|
points was successful but a second run of scan_mounts still shows some of the unmount
|
|
mount points still mounted.
|
|
"""
|
|
try:
|
|
umount_all('/dev')
|
|
raise Exception('A GenericError was not raised during the test')
|
|
except koji.GenericError as e:
|
|
self.assertEqual(e.args[0], 'Unmounting incomplete: [\'/dev/shm\', \'/dev/mqueue\']')
|
|
|
|
def test_BaseTaskHandler_handler_not_set(self):
|
|
""" Tests that an exception is thrown when the handler function is not overwritten
|
|
by the child class.
|
|
"""
|
|
obj = BadTask(123, 'some_method', ['random_arg'], None, None,
|
|
(self.get_tmp_dir_path('BadTask')))
|
|
try:
|
|
obj.handler()
|
|
raise Exception('The NotImplementedError exception was not raised')
|
|
except NotImplementedError as e:
|
|
self.assertEqual(e.__class__.__name__, 'NotImplementedError')
|
|
|
|
def test_BaseTaskHandler_weight_default(self):
|
|
""" Tests that the weight function returns 1.0 when _taskWeight is not set in the child
|
|
class' definition.
|
|
"""
|
|
obj = TaskNoWeightTest(123, 'some_method', ['random_arg'], None, None,
|
|
(self.get_tmp_dir_path('TaskNoWeightTest')))
|
|
self.assertEqual(obj.weight(), 1.0)
|
|
|
|
def test_BaseTaskHandler_weight_set(self):
|
|
""" Tests that the weight function returns the value of _taskWeight when it is set in the
|
|
child class' definition.
|
|
"""
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None,
|
|
(self.get_tmp_dir_path('TaskTest')))
|
|
self.assertEqual(obj.weight(), 5.2)
|
|
|
|
def test_BaseTaskHandler_createWorkdir_workdir_not_defined(self):
|
|
""" Tests that the createWorkdir function does nothing when the workdir member
|
|
variable is set to None.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
obj.workdir = None
|
|
obj.createWorkdir()
|
|
self.assertEqual(path.isdir(temp_path), False)
|
|
|
|
# This patch removes the dependence on removeWorkdir functioning
|
|
@patch('{0}.TaskTest.removeWorkdir'.format(__name__))
|
|
def test_BaseTaskHandler_createWorkdir(self, mock_removeWorkDir):
|
|
""" Tests that the createWorkdir function creates a folder based on the path given to the
|
|
workdir member variable.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
obj.createWorkdir()
|
|
self.assertEqual(path.isdir(temp_path), True)
|
|
|
|
def test_BaseTaskHandler_removeWorkdir(self):
|
|
""" Tests that the removeWOrkdir function deletes a folder based on the path given to the
|
|
workdir member variable.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
self.assertEqual(path.isdir(temp_path), True)
|
|
obj.removeWorkdir()
|
|
self.assertEqual(path.isdir(temp_path), False)
|
|
|
|
def test_BaseTaskHandler_wait_all_done(self):
|
|
""" Tests that the wait function returns the subtask results of when
|
|
the taskWait function returns only two finished tasks.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(12345678, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
obj.session = Mock()
|
|
obj.session.host.taskSetWait.return_value = None
|
|
obj.session.host.taskWait.return_value = [[1551234, 1591234], []]
|
|
taskWaitResults = [
|
|
['1551234', {
|
|
'brootid': 2342345,
|
|
'logs': ['tasks/5678/12345678/root.log',
|
|
'tasks/5678/12345678/state.log',
|
|
'tasks/5678/12345678/build.log'],
|
|
'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm'
|
|
}],
|
|
|
|
['1591234', {
|
|
'brootid': 1231234,
|
|
'logs': ['tasks/6789/2345678/root.log',
|
|
'tasks/6789/2345678/state.log',
|
|
'tasks/6789/2345678/build.log'],
|
|
'rpms': ['tasks/6789/2345678/some_other_package-doc-1.2.3p5-25.el7.noarch.rpm'],
|
|
'srpms': ['tasks/6789/2345678/some_other_package-1.2.3p5-25.el7.src.rpm']
|
|
}]
|
|
]
|
|
|
|
obj.session.host.taskWaitResults.return_value = taskWaitResults
|
|
self.assertEqual(obj.wait([1551234, 1591234]), dict(taskWaitResults))
|
|
obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234])
|
|
obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234, 1591234],
|
|
canfail=None)
|
|
|
|
def test_BaseTaskHandler_wait_some_not_done(self):
|
|
""" Tests that the wait function returns the one finished subtask results of
|
|
when the taskWait function returns one finished task and one unfinished
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(12345678, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
obj.session = Mock()
|
|
obj.session.host.taskSetWait.return_value = None
|
|
obj.session.host.taskWait.return_value = [[1551234], [1591234]]
|
|
taskWaitResults = [
|
|
['1551234', {
|
|
'brootid': 2342345,
|
|
'logs': ['tasks/5678/12345678/root.log',
|
|
'tasks/5678/12345678/state.log',
|
|
'tasks/5678/12345678/build.log'],
|
|
'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm'
|
|
}]
|
|
]
|
|
|
|
obj.session.host.taskWaitResults.return_value = taskWaitResults
|
|
self.assertEqual(obj.wait([1551234, 1591234]), dict(taskWaitResults))
|
|
obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234])
|
|
obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234], canfail=None)
|
|
|
|
@patch('signal.pause', return_value=None)
|
|
def test_BaseTaskHandler_wait_some_not_done_all_set(self, mock_signal_pause):
|
|
""" Tests that the wait function returns the two subtask results since the
|
|
all kwarg is set to True.
|
|
The taskWait function should first return one finished and one unfinished task,
|
|
then the second time it should return two finished tasks.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(12345678, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
obj.session = Mock()
|
|
obj.session.host.taskSetWait.return_value = None
|
|
obj.session.host.taskWait.side_effect = [[[1551234], [1591234]], [[1551234, 1591234], []]]
|
|
taskWaitResults = [
|
|
['1551234', {
|
|
'brootid': 2342345,
|
|
'logs': ['tasks/5678/12345678/root.log',
|
|
'tasks/5678/12345678/state.log',
|
|
'tasks/5678/12345678/build.log'],
|
|
'srpm': 'tasks/5678/12345678/some_package-1.2.3p5-25.src.rpm'
|
|
}],
|
|
|
|
['1591234', {
|
|
'brootid': 1231234,
|
|
'logs': ['tasks/6789/2345678/root.log',
|
|
'tasks/6789/2345678/state.log',
|
|
'tasks/6789/2345678/build.log'],
|
|
'rpms': ['tasks/6789/2345678/some_other_package-doc-1.2.3p5-25.el7.noarch.rpm'],
|
|
'srpms': ['tasks/6789/2345678/some_other_package-1.2.3p5-25.el7.src.rpm']
|
|
}]
|
|
]
|
|
|
|
obj.session.getTaskResult.side_effect
|
|
|
|
obj.session.host.taskWaitResults.return_value = taskWaitResults
|
|
self.assertEqual(obj.wait([1551234, 1591234], all=True), dict(taskWaitResults))
|
|
obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234])
|
|
obj.session.host.taskWait.assert_has_calls([call(12345678), call(12345678)])
|
|
mock_signal_pause.assert_called_once_with()
|
|
obj.session.host.taskWaitResults.assert_called_once_with(12345678, [1551234, 1591234],
|
|
canfail=None)
|
|
|
|
def test_BaseTaskHandler_wait_some_not_done_all_set_failany_set_failed_task(self):
|
|
""" Tests that the wait function raises an exception when one of the subtask fails
|
|
when the failany flag is set to True.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(12345678, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
obj.session = Mock()
|
|
obj.session.host.taskSetWait.return_value = None
|
|
obj.session.host.taskWait.side_effect = [[[1551234], [1591234]], [[1551234, 1591234], []]]
|
|
obj.session.getTaskResult.side_effect = koji.GenericError(
|
|
'Uh oh, we\'ve got a problem here!')
|
|
try:
|
|
obj.wait([1551234, 1591234], all=True, failany=True)
|
|
raise Exception('A GeneralError was not raised.')
|
|
except koji.GenericError as e:
|
|
self.assertEqual(e.args[0], 'Uh oh, we\'ve got a problem here!')
|
|
obj.session.host.taskSetWait.assert_called_once_with(12345678, [1551234, 1591234])
|
|
|
|
@patch('time.time')
|
|
@patch('time.sleep')
|
|
@patch('signal.pause')
|
|
def test_BaseTaskHandler_wait_timeout(self, pause, sleep, time):
|
|
"""Tests timeout behavior in the wait function"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(95, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
obj.session = MagicMock()
|
|
obj.session.host.taskWait.return_value = [[], [99, 100, 101]]
|
|
time.side_effect = list(range(0, 4000, 60))
|
|
if six.PY3:
|
|
sigtimedwait = patch('signal.sigtimedwait')
|
|
sigtimedwait.start()
|
|
|
|
try:
|
|
obj.wait([99, 100, 101], timeout=3600)
|
|
raise Exception('A GenericError was not raised.')
|
|
except koji.GenericError as e:
|
|
self.assertEqual(e.args[0][:24], 'Subtasks timed out after')
|
|
|
|
if six.PY3:
|
|
sigtimedwait.stop()
|
|
|
|
obj.session.host.taskSetWait.assert_called_once_with(95, [99, 100, 101])
|
|
obj.session.cancelTaskChildren.assert_called_once_with(95)
|
|
obj.session.getTaskResult.assert_not_called()
|
|
pause.assert_not_called()
|
|
|
|
@patch('time.time')
|
|
@patch('time.sleep')
|
|
@patch('signal.pause')
|
|
def test_BaseTaskHandler_wait_avoid_timeout(self, pause, sleep, time):
|
|
"""Tests that timeout does not happen if tasks finish in time"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(95, 'some_method', ['random_arg'], None, None, temp_path)
|
|
makedirs(temp_path)
|
|
obj.session = MagicMock()
|
|
time.side_effect = list(range(0, 4000, 20))
|
|
# time ticks every 20s for a little over an "hour"
|
|
# code checks time 3x each cycle (twice directly, once via logging)
|
|
# so each cycle is a "minute"
|
|
# report all unfinished for most of an hour
|
|
taskWait_returns = [[[], [99, 100, 101]]] * 50
|
|
# and then report all done
|
|
taskWait_returns.append([[99, 100, 101], []])
|
|
obj.session.host.taskWait.side_effect = taskWait_returns
|
|
|
|
if six.PY3:
|
|
sigtimedwait = patch('signal.sigtimedwait')
|
|
sigtimedwait.start()
|
|
|
|
obj.wait([99, 100, 101], timeout=3600)
|
|
|
|
if six.PY3:
|
|
sigtimedwait.stop()
|
|
|
|
obj.session.host.taskSetWait.assert_called_once_with(95, [99, 100, 101])
|
|
obj.session.cancelTaskChildren.assert_not_called()
|
|
pause.assert_not_called()
|
|
|
|
def test_BaseTaskHandler_getUploadDir(self):
|
|
""" Tests that the getUploadDir function returns the appropriate path based
|
|
on the id of the handler.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
self.assertEqual(obj.getUploadDir(), 'tasks/123/123')
|
|
|
|
# This patch removes the dependence on getUploadDir functioning
|
|
@patch('{0}.TaskTest.getUploadDir'.format(__name__), return_value='tasks/123/123')
|
|
def test_BaseTaskHandler_uploadFile(self, mock_getUploadDir):
|
|
""" Tests that the uploadFile function calls the uploadWrapper function
|
|
on the session member variable with the correct input.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
temp_file = path.join(temp_path, 'test.txt')
|
|
with open(temp_file, 'wt') as temp_file_handler:
|
|
temp_file_handler.write('Test')
|
|
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
obj.session = Mock()
|
|
self.assertEqual(obj.uploadFile(temp_file), None)
|
|
obj.session.uploadWrapper.assert_called_once_with(temp_file, 'tasks/123/123', None,
|
|
volume=None)
|
|
|
|
# This patch removes the dependence on getUploadDir functioning
|
|
@patch('{0}.TaskTest.getUploadDir'.format(__name__), return_value='tasks/123/123')
|
|
def test_BaseTaskHandler_uploadFile_no_content(self, mock_getUploadDir):
|
|
""" Tests that the uploadFile function calls the uploadWrapper function
|
|
on the session member variable without including empty files.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
temp_file = path.join(temp_path, 'test.txt')
|
|
temp_file_handler = open(temp_file, 'w')
|
|
temp_file_handler.close()
|
|
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
obj.session = Mock()
|
|
self.assertEqual(obj.uploadFile(temp_file), None)
|
|
obj.session.uploadWrapper.assert_called_once()
|
|
|
|
def test_BaseTaskHandler_uploadTree(self):
|
|
""" Tests that the uploadTree function calls the uploadFile function
|
|
with the correct parameters.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
dummy_dir = path.join(temp_path, 'some_directory')
|
|
makedirs(dummy_dir)
|
|
|
|
dummy_file = path.join(temp_path, 'test.txt')
|
|
with open(dummy_file, 'wt') as temp_file_handler:
|
|
temp_file_handler.write('Test')
|
|
|
|
dummy_file2 = path.join(dummy_dir, 'test2.txt')
|
|
with open(dummy_file2, 'wt') as temp_file_handler2:
|
|
temp_file_handler2.write('Test2')
|
|
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
obj.uploadFile = Mock()
|
|
obj.uploadFile.return_value = None
|
|
self.assertEqual(obj.uploadTree(temp_path), None)
|
|
obj.uploadFile.assert_has_calls([call(dummy_file, '', volume=None),
|
|
call(dummy_file2, 'some_directory', volume=None)])
|
|
|
|
@patch('os.lchown', return_value=None)
|
|
def test_BaseTaskHandler_chownTree(self, mock_lchown):
|
|
""" Tests that the chownTree functions as expected on dummy files created
|
|
in a temp directory
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
dummy_file = path.join(temp_path, 'test.txt')
|
|
dummy_file_handler = open(dummy_file, 'w')
|
|
dummy_file_handler.close()
|
|
|
|
dummy_file2 = path.join(temp_path, 'test2.txt')
|
|
dummy_file_handler2 = open(dummy_file2, 'w')
|
|
dummy_file_handler2.close()
|
|
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
self.assertEqual(obj.chownTree(temp_path, 2, 0), None)
|
|
mock_lchown.assert_has_calls([call(temp_path, 2, 0), call(dummy_file2, 2, 0),
|
|
call(dummy_file, 2, 0)], any_order=True)
|
|
|
|
def test_BaseTaskHandler_localPath_file_exists(self):
|
|
""" Tests the localPath function to ensure that when a file exists,
|
|
it returns that path without trying to download it.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
local_folder = path.join(temp_path, 'local')
|
|
makedirs(local_folder)
|
|
|
|
dummy_file = path.join(local_folder, 'test.txt')
|
|
dummy_file_handler = open(dummy_file, 'w')
|
|
dummy_file_handler.close()
|
|
options = Mock()
|
|
options.topurl = 'https://www.domain.local'
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, options, temp_path)
|
|
self.assertEqual(obj.localPath('test.txt'), dummy_file)
|
|
|
|
@requests_mock.Mocker()
|
|
def test_BaseTaskHandler_localPath_no_file(self, m_requests):
|
|
"""
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
local_folder = path.join(temp_path, 'local')
|
|
makedirs(local_folder)
|
|
|
|
target_file_path = path.join(local_folder, 'test.txt')
|
|
|
|
options = Mock()
|
|
options.topurl = 'https://www.domain.local'
|
|
url = options.topurl + '/test.txt'
|
|
m_requests.register_uri('GET', url, text='Important things\nSome more important things\n')
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, options, temp_path)
|
|
|
|
self.assertEqual(obj.localPath('test.txt'), target_file_path)
|
|
self.assertEqual(m_requests.call_count, 1)
|
|
self.assertEqual(m_requests.request_history[0].url, url)
|
|
|
|
def test_BaseTaskHandler_localPath_no_topurl(self):
|
|
""" Tests that the localPath function returns a path when options.topurl is not defined.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
options = Mock()
|
|
options.topurl = None
|
|
options.topdir = self.tempdir
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, options, temp_path)
|
|
|
|
self.assertEqual(obj.localPath('test.txt'), path.join(self.tempdir, 'test.txt'))
|
|
|
|
def test_BaseTaskHandler_find_arch(self):
|
|
""" Tests that the find_arch function returns the input for arch when
|
|
the input is not "noarch".
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
self.assertEqual(obj.find_arch('x86_64', None, None), 'x86_64')
|
|
|
|
def test_BaseTaskHandler_find_arch_noarch_bad_host(self):
|
|
""" Tests that the find_arch function raises an exception when
|
|
the host parameter doesn't contain a value for the arches key.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
host = {'arches': None, 'name': 'test.domain.local'}
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
try:
|
|
obj.find_arch('noarch', host, None)
|
|
raise Exception('The BuildError Exception was not raised')
|
|
except koji.BuildError as e:
|
|
self.assertEqual(e.args[0], 'No arch list for this host: test.domain.local')
|
|
|
|
def test_BaseTaskHandler_find_arch_noarch_bad_tag(self):
|
|
""" Tests that the find_arch function raises an exception when the tag parameter
|
|
doesn't contain a value for the arches key.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
host = {'arches': 'x86_64', 'name': 'test.domain.local'}
|
|
tag = {'arches': None, 'name': 'some_package-1.2-build'}
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
try:
|
|
obj.find_arch('noarch', host, tag)
|
|
raise Exception('The BuildError Exception was not raised')
|
|
except koji.BuildError as e:
|
|
self.assertEqual(e.args[0], 'No arch list for tag: some_package-1.2-build')
|
|
|
|
def test_BaseTaskHandler_find_arch_noarch(self):
|
|
""" Tests that the find_arch function finds a match of x86_64 when the host
|
|
only supports x86_64 and the tag supports x86_64 and aarch64.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
host = {'arches': 'x86_64', 'name': 'test.domain.local'}
|
|
tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'}
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
self.assertEqual(obj.find_arch('noarch', host, tag), 'x86_64')
|
|
|
|
def test_BaseTaskHandler_find_arch__noarch_no_match(self):
|
|
""" Tests that the find_arch function raises an exception when there isn't
|
|
a common arch supported between the host and the tag.
|
|
"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
host = {'arches': 'i386', 'name': 'test.domain.local'}
|
|
tag = {'arches': 'x86_64 aarch64', 'name': 'some_package-1.2-build'}
|
|
obj = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
try:
|
|
obj.find_arch('noarch', host, tag)
|
|
raise Exception('The BuildError Exception was not raised')
|
|
except koji.BuildError as e:
|
|
self.assertEqual(e.args[0],
|
|
('host test.domain.local (i386) does not support any arches '
|
|
'of tag some_package-1.2-build (aarch64, x86_64)'))
|
|
|
|
@patch('koji.util.RepoWatcher')
|
|
def test_getRepo_no_wait_task(self, RepoWatcher):
|
|
""" Tests that the getRepo method does not wait if repo is available"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
repo_dict = {
|
|
'create_event': 13635166,
|
|
'create_ts': 1469039671.5743899,
|
|
'creation_time': '2016-07-20 18:34:31.574386',
|
|
'id': 1630631,
|
|
'state': 1
|
|
}
|
|
|
|
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
handler.session = mock.MagicMock()
|
|
handler.wait = mock.MagicMock()
|
|
watcher = mock.MagicMock()
|
|
watcher.getRepo.return_value = repo_dict
|
|
RepoWatcher.return_value = watcher
|
|
|
|
result = handler.getRepo(8472)
|
|
|
|
handler.session.host.subtask.assert_not_called()
|
|
handler.wait.assert_not_called()
|
|
self.assertEqual(result, repo_dict)
|
|
|
|
@patch('koji.util.RepoWatcher')
|
|
def test_getRepo_last_event(self, RepoWatcher):
|
|
""" Tests that the getRepo method uses min_event='last' when requested"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
repo_dict = {
|
|
'create_event': 13635166,
|
|
'create_ts': 1469039671.5743899,
|
|
'creation_time': '2016-07-20 18:34:31.574386',
|
|
'id': 1630631,
|
|
'state': 1
|
|
}
|
|
|
|
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
handler.session = mock.MagicMock()
|
|
handler.wait = mock.MagicMock()
|
|
watcher = mock.MagicMock()
|
|
watcher.getRepo.return_value = repo_dict
|
|
RepoWatcher.return_value = watcher
|
|
|
|
result = handler.getRepo(8472, wait=True)
|
|
|
|
RepoWatcher.assert_called_once_with(handler.session, 8472, nvrs=None, min_event='last', logger=handler.logger)
|
|
handler.session.host.subtask.assert_not_called()
|
|
handler.wait.assert_not_called()
|
|
self.assertEqual(result, repo_dict)
|
|
|
|
@patch('koji.util.RepoWatcher')
|
|
def test_getRepo_wait_task(self, RepoWatcher):
|
|
""" Tests that the getRepo function waits for subtask if repo not immediately available"""
|
|
temp_path = self.get_tmp_dir_path('TaskTest')
|
|
makedirs(temp_path)
|
|
|
|
repo_dict = {
|
|
'create_event': 13635166,
|
|
'create_ts': 1469039671.5743899,
|
|
'creation_time': '2016-07-20 18:34:31.574386',
|
|
'id': 1630631,
|
|
'state': 1
|
|
}
|
|
|
|
handler = TaskTest(123, 'some_method', ['random_arg'], None, None, temp_path)
|
|
handler.session = mock.MagicMock()
|
|
handler.session.host.subtask.return_value = 'TASKID'
|
|
handler.wait = mock.MagicMock()
|
|
handler.wait.return_value = {'TASKID': repo_dict}
|
|
watcher = mock.MagicMock()
|
|
watcher.getRepo.return_value = None
|
|
RepoWatcher.return_value = watcher
|
|
|
|
result = handler.getRepo(8472)
|
|
|
|
handler.session.host.subtask.assert_called_once()
|
|
handler.wait.assert_called_once_with('TASKID')
|
|
self.assertEqual(result, repo_dict)
|
|
|
|
def test_FakeTask_handler(self):
|
|
""" Tests that the FakeTest handler can be instantiated and returns 42 when run.
|
|
"""
|
|
obj = FakeTask(123, 'someMethod', ['random_arg'], None, None,
|
|
(self.get_tmp_dir_path('FakeTask')))
|
|
self.assertEqual(obj.run(), 42)
|
|
|
|
@patch('time.sleep')
|
|
def test_SleepTask_handler(self, mock_sleep):
|
|
""" Tests that the SleepTask handler can be instantiated and runs appropriately
|
|
based on the input.
|
|
"""
|
|
obj = SleepTask(123, 'sleep', [5], None, None, (self.get_tmp_dir_path('SleepTask')))
|
|
obj.run()
|
|
mock_sleep.assert_called_once_with(5)
|
|
|
|
@patch('os.spawnvp')
|
|
def test_ForkTask_handler(self, mock_spawnvp):
|
|
""" Tests that the ForkTask handler can be instantiated and runs appropriately
|
|
based on the input.
|
|
"""
|
|
obj = ForkTask(123, 'fork', [1, 20], None, None, (self.get_tmp_dir_path('ForkTask')))
|
|
obj.run()
|
|
mock_spawnvp.assert_called_once_with(1, 'sleep', ['sleep', '20'])
|
|
|
|
@patch('signal.pause', return_value=None)
|
|
@patch('time.sleep')
|
|
def test_WaitTestTask_handler(self, mock_sleep, mock_signal_pause):
|
|
""" Tests that the WaitTestTask handler can be instantiated and runs appropriately
|
|
based on the input.
|
|
Specifically, that forking works and canfail behaves correctly.
|
|
"""
|
|
self.mock_subtask_id = 1
|
|
|
|
def mock_subtask(method, arglist, id, **opts):
|
|
self.assertEqual(method, 'sleep')
|
|
task_id = self.mock_subtask_id
|
|
self.mock_subtask_id += 1
|
|
obj = SleepTask(task_id, 'sleep', arglist, None, None, (self.get_tmp_dir_path('SleepTask')))
|
|
obj.run()
|
|
return task_id
|
|
|
|
mock_taskWait = [
|
|
[[], [1, 2, 3, 4]],
|
|
[[3, 4], [1, 2]],
|
|
[[1, 2, 3, 4], []],
|
|
]
|
|
|
|
def mock_getTaskResult(task_id):
|
|
if task_id == 4:
|
|
raise koji.GenericError()
|
|
|
|
obj = WaitTestTask(123, 'waittest', [3], None, None, (self.get_tmp_dir_path('WaitTestTask')))
|
|
obj.session = Mock()
|
|
obj.session.host.subtask.side_effect = mock_subtask
|
|
obj.session.getTaskResult.side_effect = mock_getTaskResult
|
|
obj.session.host.taskWait.side_effect = mock_taskWait
|
|
obj.session.host.taskWaitResults.return_value = [['1', {}], ['2', {}], ['3', {}],
|
|
['4', {}], ]
|
|
obj.run()
|
|
# self.assertEqual(mock_sleep.call_count, 4)
|
|
obj.session.host.taskSetWait.assert_called_once()
|
|
obj.session.host.taskWait.assert_has_calls([call(123), call(123), call(123)])
|
|
# getTaskResult should be called in 2nd round only for task 3, as 4
|
|
# will be skipped as 'canfail'
|
|
obj.session.getTaskResult.assert_has_calls([call(3)])
|
|
|
|
|
|
class TestSafeRmtree(unittest.TestCase):
|
|
@patch('os.path.exists', return_value=True)
|
|
@patch('os.path.isfile', return_value=True)
|
|
@patch('os.path.islink', return_value=False)
|
|
@patch('os.remove')
|
|
@patch('koji.util.rmtree')
|
|
def test_safe_rmtree_file(self, rmtree, remove, islink, isfile, exists):
|
|
""" Tests that the koji.util.rmtree function returns nothing when
|
|
the path parameter is a file.
|
|
"""
|
|
path = '/mnt/folder/some_file'
|
|
self.assertEqual(safe_rmtree(path, False, True), 0)
|
|
isfile.assert_called_once_with(path)
|
|
islink.assert_not_called()
|
|
exists.assert_not_called()
|
|
remove.assert_called_once_with(path)
|
|
rmtree.assert_not_called()
|
|
|
|
@patch('os.path.exists', return_value=True)
|
|
@patch('os.path.isfile', return_value=False)
|
|
@patch('os.path.islink', return_value=True)
|
|
@patch('os.remove')
|
|
@patch('koji.util.rmtree')
|
|
def test_rmtree_link(self, rmtree, remove, islink, isfile, exists):
|
|
""" Tests that the koji.util.rmtree function returns nothing when
|
|
the path parameter is a link.
|
|
"""
|
|
path = '/mnt/folder/some_link'
|
|
self.assertEqual(safe_rmtree(path, False, True), 0)
|
|
isfile.assert_called_once_with(path)
|
|
islink.assert_called_once_with(path)
|
|
exists.assert_not_called()
|
|
remove.assert_called_once_with(path)
|
|
rmtree.assert_not_called()
|
|
|
|
@patch('os.path.exists', return_value=False)
|
|
@patch('os.path.isfile', return_value=False)
|
|
@patch('os.path.islink', return_value=False)
|
|
@patch('os.remove')
|
|
@patch('koji.util.rmtree')
|
|
def test_rmtree_does_not_exist(self, rmtree, remove, islink, isfile, exists):
|
|
""" Tests that the koji.util.rmtree function returns nothing if the path does not exist.
|
|
"""
|
|
path = '/mnt/folder/some_file'
|
|
self.assertEqual(safe_rmtree(path, False, True), 0)
|
|
isfile.assert_called_once_with(path)
|
|
islink.assert_called_once_with(path)
|
|
exists.assert_called_once_with(path)
|
|
remove.assert_not_called()
|
|
rmtree.assert_not_called()
|
|
|
|
@patch('os.path.exists', return_value=True)
|
|
@patch('os.path.isfile', return_value=False)
|
|
@patch('os.path.islink', return_value=False)
|
|
@patch('os.remove')
|
|
@patch('koji.util.rmtree')
|
|
def test_rmtree_directory(self, rmtree, remove, islink, isfile, exists):
|
|
""" Tests that the koji.util.rmtree function returns nothing when the path is a directory.
|
|
"""
|
|
path = '/mnt/folder'
|
|
self.assertEqual(safe_rmtree(path, False, True), 0)
|
|
isfile.assert_called_once_with(path)
|
|
islink.assert_called_once_with(path)
|
|
exists.assert_called_once_with(path)
|
|
remove.assert_not_called()
|
|
rmtree.assert_called_once_with(path)
|
|
|
|
@patch('os.path.exists', return_value=True)
|
|
@patch('os.path.isfile', return_value=False)
|
|
@patch('os.path.islink', return_value=False)
|
|
@patch('os.remove')
|
|
@patch('koji.util.rmtree')
|
|
def test_rmtree_directory_scrub_file_failure(self, rmtree, remove, islink, isfile, exists):
|
|
""" Tests that the koji.util.rmtree function returns a GeneralException when
|
|
the path parameter is a directory
|
|
and the scrub of the files in the directory fails.
|
|
"""
|
|
rmtree.side_effect = koji.GenericError('xyz')
|
|
path = '/mnt/folder'
|
|
try:
|
|
safe_rmtree(path, False, 1)
|
|
raise Exception('A GenericError was not raised during the test')
|
|
except koji.GenericError as e:
|
|
self.assertEqual(e.args[0], 'xyz')
|
|
isfile.assert_called_once_with(path)
|
|
islink.assert_called_once_with(path)
|
|
exists.assert_called_once_with(path)
|
|
remove.assert_not_called()
|
|
rmtree.assert_called_once_with(path)
|
|
|
|
@patch('os.path.exists', return_value=True)
|
|
@patch('os.path.isfile', return_value=False)
|
|
@patch('os.path.islink', return_value=False)
|
|
@patch('os.remove')
|
|
@patch('koji.util.rmtree')
|
|
def test_safe_rmtree_directory_scrub_directory_failure(
|
|
self, rmtree, remove, islink, isfile, exists):
|
|
""" Tests that the koji.util.rmtree function returns a GeneralException when
|
|
the path parameter is a directory
|
|
and the scrub of the directories in the directory fails.
|
|
"""
|
|
rmtree.side_effect = OSError('xyz')
|
|
path = '/mnt/folder'
|
|
try:
|
|
safe_rmtree(path, False, True)
|
|
raise Exception('An OSError was not raised during the test')
|
|
except OSError as e:
|
|
self.assertEqual(e.args[0], 'xyz')
|
|
|
|
isfile.assert_called_once_with(path)
|
|
islink.assert_called_once_with(path)
|
|
exists.assert_called_once_with(path)
|
|
remove.assert_not_called()
|
|
rmtree.assert_called_once_with(path)
|