Initial server source import
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP_DIR = ROOT / 'app'
|
||||
sys.path.insert(0, str(APP_DIR))
|
||||
|
||||
import supervisor
|
||||
from lifecycle_authority import (
|
||||
DISCOVERY_PRODUCER_ROLE,
|
||||
DISCOVERY_PRODUCER_SOURCES,
|
||||
)
|
||||
from runtime_security import ensure_private_directory, harden_private_file
|
||||
|
||||
|
||||
def producer_fixture(temp_dir, source='gitlab', source_config=None):
|
||||
ensure_private_directory(temp_dir, reject_reparse=True)
|
||||
config_path = os.path.join(temp_dir, 'config.yaml')
|
||||
Path(config_path).write_text('{}\n', encoding='ascii')
|
||||
harden_private_file(config_path)
|
||||
log_dir = os.path.join(temp_dir, 'logs')
|
||||
state_dir = os.path.join(temp_dir, 'state')
|
||||
ensure_private_directory(log_dir, reject_reparse=True)
|
||||
ensure_private_directory(state_dir, reject_reparse=True)
|
||||
canonical_dsn = 'postgresql://managed@127.0.0.1/truf'
|
||||
gate = supervisor.DependencyGate(ready=True, database_url=canonical_dsn)
|
||||
child_environment = {
|
||||
'TRUF_SUPERVISOR_TOKEN': 'trusted-token',
|
||||
'TRUF_SUPERVISOR_CHILD_KIND': DISCOVERY_PRODUCER_ROLE,
|
||||
'TRUF_MANAGED_POSTGRES_DSN': canonical_dsn,
|
||||
}
|
||||
producer = supervisor.ManagedDiscoveryProducer(
|
||||
source, config_path, str(APP_DIR), temp_dir,
|
||||
{
|
||||
'log_dir': log_dir,
|
||||
'state_dir': state_dir,
|
||||
'defaults': {'enabled': True, 'restart': True},
|
||||
'sources': {source: dict(source_config or {})},
|
||||
},
|
||||
{'enabled': True},
|
||||
dependency_gate=gate,
|
||||
child_environment=child_environment,
|
||||
)
|
||||
return producer, canonical_dsn
|
||||
|
||||
|
||||
class DiscoveryProducerSupervisorTests(unittest.TestCase):
|
||||
def test_allowlist_and_authenticated_one_shot_command_are_exact(self):
|
||||
self.assertEqual(
|
||||
DISCOVERY_PRODUCER_SOURCES,
|
||||
('gitlab', 'dockerhub', 'huggingface'),
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
for source in DISCOVERY_PRODUCER_SOURCES:
|
||||
with self.subTest(source=source):
|
||||
producer, _ = producer_fixture(temp_dir, source=source)
|
||||
command = producer.build_command()
|
||||
self.assertEqual(command[:4], [sys.executable, '-I', '-S', '-B'])
|
||||
self.assertEqual(Path(command[4]).name, 'child_bootstrap.py')
|
||||
self.assertEqual(command[5:7], [DISCOVERY_PRODUCER_ROLE, '--'])
|
||||
self.assertEqual(command[-5:], [
|
||||
'--config', producer.config_path, '--source', source, '--once',
|
||||
])
|
||||
self.assertTrue(producer.once)
|
||||
self.assertTrue(producer.repeat)
|
||||
self.assertEqual(producer.producer_id, f'discovery-producer:{source}')
|
||||
self.assertTrue(producer.log_path.endswith(
|
||||
f'discovery-producer-{source}.log',
|
||||
))
|
||||
self.assertTrue(producer.state_path.endswith(
|
||||
f'runner_state_{source}.json',
|
||||
))
|
||||
|
||||
with self.assertRaisesRegex(ValueError, 'allowlist'):
|
||||
producer_fixture(temp_dir, source='github')
|
||||
|
||||
def test_environment_strips_overrides_and_restores_immutable_authority(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
producer, canonical_dsn = producer_fixture(temp_dir, source_config={
|
||||
'enabled': True,
|
||||
'env': {
|
||||
'PGPASSWORD': 'forged',
|
||||
'SCANNER_DB_URL': 'postgresql://forged',
|
||||
'TRUF_SUPERVISOR_TOKEN': 'forged-token',
|
||||
'TRUF_SUPERVISOR_CHILD_KIND': 'scanner',
|
||||
'TRUF_MANAGED_POSTGRES_DSN': 'postgresql://forged',
|
||||
'SAFE_PROVIDER_COMPAT': 'retained',
|
||||
},
|
||||
})
|
||||
environment = producer.build_env()
|
||||
self.assertNotIn('PGPASSWORD', environment)
|
||||
self.assertEqual(environment['SCANNER_DB_URL'], canonical_dsn)
|
||||
self.assertEqual(environment['DATABASE_URL'], canonical_dsn)
|
||||
self.assertEqual(environment['TRUF_MANAGED_POSTGRES_DSN'], canonical_dsn)
|
||||
self.assertEqual(environment['TRUF_SUPERVISOR_TOKEN'], 'trusted-token')
|
||||
self.assertEqual(environment['TRUF_SUPERVISOR_CHILD_KIND'], DISCOVERY_PRODUCER_ROLE)
|
||||
self.assertEqual(environment['TRUF_DISCOVERY_SOURCE'], 'gitlab')
|
||||
self.assertEqual(environment['SAFE_PROVIDER_COMPAT'], 'retained')
|
||||
self.assertNotIn('SCANNER_SKIP_STARTUP_CLEANUP', environment)
|
||||
|
||||
def test_reconfigure_cannot_disable_one_shot_repeat_mode(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
producer, _ = producer_fixture(temp_dir)
|
||||
producer.reconfigure(
|
||||
temp_dir,
|
||||
{
|
||||
'log_dir': os.path.join(temp_dir, 'logs'),
|
||||
'state_dir': os.path.join(temp_dir, 'state'),
|
||||
'defaults': {'enabled': True},
|
||||
},
|
||||
{'enabled': True, 'once': False, 'repeat': False, 'extra_args': ['--loop']},
|
||||
)
|
||||
self.assertTrue(producer.once)
|
||||
self.assertTrue(producer.repeat)
|
||||
self.assertNotIn('--loop', producer.build_command())
|
||||
|
||||
def test_structured_state_is_bounded_and_signature_remains_nine_fields(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
producers = []
|
||||
for source in DISCOVERY_PRODUCER_SOURCES:
|
||||
producer, _ = producer_fixture(temp_dir, source=source)
|
||||
producers.append(producer)
|
||||
state = {
|
||||
'sources': {
|
||||
source: {
|
||||
'last_status': 'failed',
|
||||
'last_cycle_result': {
|
||||
'status': 'must-not-leak',
|
||||
'fetched_count': 7,
|
||||
'queued_new_count': 5,
|
||||
'queued_updated_count': 2,
|
||||
'provider_error': 'must-not-leak',
|
||||
},
|
||||
'last_discovery_success_at': 'must-not-leak',
|
||||
'last_error_category': 'must-not-leak',
|
||||
'raw_error': 'must-not-leak',
|
||||
},
|
||||
},
|
||||
'token': 'must-not-leak',
|
||||
}
|
||||
Path(producer.state_path).write_text(json.dumps(state), encoding='utf-8')
|
||||
snapshot = producer.structured_state()
|
||||
serialized = json.dumps(snapshot, sort_keys=True)
|
||||
self.assertEqual(snapshot['id'], f'discovery-producer:{source}')
|
||||
self.assertEqual(snapshot['source'], source)
|
||||
self.assertEqual(snapshot['role'], DISCOVERY_PRODUCER_ROLE)
|
||||
self.assertEqual(snapshot['process_state'], 'stopped')
|
||||
self.assertEqual(snapshot['interval_seconds'], producer.interval)
|
||||
self.assertTrue(snapshot['restart_enabled'])
|
||||
self.assertEqual(snapshot['allowed_actions'], [
|
||||
'start', 'stop', 'restart', 'pause', 'resume', 'set-interval',
|
||||
])
|
||||
self.assertEqual(snapshot['safe_error_category'], 'runtime_error')
|
||||
self.assertIsNone(snapshot['last_successful_discovery_at'])
|
||||
self.assertEqual(snapshot['last_cycle_result'], {
|
||||
'status': 'unknown',
|
||||
'fetched_count': 7,
|
||||
'queued_new_count': 5,
|
||||
'queued_updated_count': 2,
|
||||
})
|
||||
self.assertNotIn('must-not-leak', serialized)
|
||||
signature = supervisor.table_signature(producers)
|
||||
self.assertEqual(len(signature), len(DISCOVERY_PRODUCER_SOURCES))
|
||||
self.assertTrue(all(len(row) == 9 for row in signature))
|
||||
|
||||
def test_typed_restart_targets_only_one_exact_producer(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
producers = [
|
||||
producer_fixture(temp_dir, source=source)[0]
|
||||
for source in DISCOVERY_PRODUCER_SOURCES
|
||||
]
|
||||
restart_methods = {}
|
||||
for producer in producers:
|
||||
replacement = mock.Mock(return_value=True)
|
||||
restart_methods[producer.producer_id] = replacement
|
||||
producer.restart_now = replacement
|
||||
|
||||
selected = producers[1]
|
||||
result = supervisor.run_managed_source_action({
|
||||
'schema': supervisor.CONTROL_SCHEMA,
|
||||
'instance_id': 'fixture-instance',
|
||||
'token': 't' * 48,
|
||||
'action': 'managed-source-action',
|
||||
'source_id': selected.producer_id,
|
||||
'source_action': 'restart',
|
||||
}, producers, {})
|
||||
self.assertEqual(result['source']['id'], selected.producer_id)
|
||||
restart_methods[selected.producer_id].assert_called_once_with()
|
||||
for producer_id, method in restart_methods.items():
|
||||
if producer_id != selected.producer_id:
|
||||
method.assert_not_called()
|
||||
|
||||
for source_id in ('discovery-producer:github', 'discovery-producer:unknown'):
|
||||
with self.subTest(source_id=source_id), self.assertRaisesRegex(
|
||||
ValueError, 'unknown',
|
||||
):
|
||||
supervisor.run_managed_source_action({
|
||||
'schema': supervisor.CONTROL_SCHEMA,
|
||||
'instance_id': 'fixture-instance',
|
||||
'token': 't' * 48,
|
||||
'action': 'managed-source-action',
|
||||
'source_id': source_id,
|
||||
'source_action': 'restart',
|
||||
}, producers, {})
|
||||
self.assertEqual(
|
||||
sum(method.call_count for method in restart_methods.values()), 1,
|
||||
)
|
||||
|
||||
def test_restart_does_not_relaunch_after_start_gate_closes(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
producer, _ = producer_fixture(temp_dir)
|
||||
gate = {'open': True}
|
||||
producer.start_gate = lambda: gate['open']
|
||||
|
||||
def stop_for_shutdown(timeout=10):
|
||||
gate['open'] = False
|
||||
return True
|
||||
|
||||
with (
|
||||
mock.patch.object(producer, 'stop', side_effect=stop_for_shutdown),
|
||||
mock.patch.object(supervisor, 'OwnedProcess') as launch,
|
||||
):
|
||||
self.assertFalse(producer.restart_now())
|
||||
launch.assert_not_called()
|
||||
self.assertEqual(
|
||||
producer.last_action_error,
|
||||
'supervisor lifecycle start gate is closed',
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user