68 lines
3.0 KiB
Python
68 lines
3.0 KiB
Python
import os
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
from datetime import datetime, timedelta, timezone
|
|
import unittest
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / 'app'))
|
|
|
|
import worker_assignment_runner as runner
|
|
from runtime_security import ensure_private_directory
|
|
|
|
|
|
@unittest.skipUnless(sys.platform.startswith('linux'), 'requires Linux renameat2')
|
|
class LinuxWorkerRunnerHandoffTests(unittest.TestCase):
|
|
def create_root(self, temporary, generation):
|
|
work = ensure_private_directory(os.path.join(temporary, 'work'), reject_reparse=True)
|
|
started = datetime.now(timezone.utc)
|
|
assignment = {
|
|
'reservation': {'reservation_id': 17},
|
|
'deadlines': {}, 'compatibility': {}, 'scan_kwargs': {},
|
|
'event_scan_options': {}, 'queue_policy': {}, 'limits': {},
|
|
'scan_policy': {}, 'execution_snapshot': {},
|
|
'execution_snapshot_sha256': 'a' * 64, 'execution_plan': {},
|
|
}
|
|
value = runner.build_runner_input(
|
|
assignment, generation=generation, slot_id=0,
|
|
scan_started_at=started.isoformat(timespec='milliseconds').replace('+00:00', 'Z'),
|
|
scan_deadline_at=(started + timedelta(seconds=60)).isoformat(
|
|
timespec='milliseconds',
|
|
).replace('+00:00', 'Z'),
|
|
)
|
|
name = runner.runner_root_name(0, 17, generation)
|
|
paths, _digest = runner.create_runner_root(work, name, value)
|
|
return work, name, paths
|
|
|
|
def test_timeout_generation_moves_atomically_to_janitor_namespace(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
work, name, paths = self.create_root(temporary, 'a' * 32)
|
|
reference = runner.transfer_runner_to_janitor(work, name, 'a' * 32)
|
|
self.assertEqual(reference, f'abandoned/{name}')
|
|
self.assertFalse(os.path.exists(paths['root']))
|
|
self.assertTrue(os.path.isdir(os.path.join(work, *reference.split('/'))))
|
|
|
|
def test_retry_handoff_is_idempotent_after_durable_move(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
work, name, _paths = self.create_root(temporary, 'b' * 32)
|
|
first = runner.transfer_runner_to_janitor(work, name, 'b' * 32)
|
|
second = runner.transfer_runner_to_janitor(work, name, 'b' * 32)
|
|
self.assertEqual(first, second)
|
|
|
|
def test_existing_destination_never_replaces_another_generation_tree(self):
|
|
with tempfile.TemporaryDirectory() as temporary:
|
|
work, name, paths = self.create_root(temporary, 'c' * 32)
|
|
abandoned = ensure_private_directory(
|
|
os.path.join(work, 'abandoned'), reject_reparse=True,
|
|
)
|
|
os.makedirs(os.path.join(abandoned, name))
|
|
with self.assertRaises(runner.RunnerProtocolError):
|
|
runner.transfer_runner_to_janitor(work, name, 'c' * 32)
|
|
self.assertTrue(os.path.isdir(paths['root']))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|