feat: connectivity redesign phase 5 — one container per connection instance
Unit Tests / test (push) Successful in 13m5s

instanceable rendering, per-instance up/down on create/delete,
store-service-installed gate, per-instance health

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 22:56:31 -04:00
parent d39c091cec
commit 603225694c
8 changed files with 688 additions and 8 deletions
+97
View File
@@ -676,5 +676,102 @@ class TestServiceComposerDeps(unittest.TestCase):
composer.up.assert_not_called()
# ── Per-connection-instance lifecycle (P5) ───────────────────────────────────
_INSTANCEABLE_TEMPLATE = (
'services:\n'
' sshuttle:\n'
' image: git.pic.ngo/roof/svc-sshuttle:latest\n'
' container_name: cell-sshuttle-${INSTANCE_ID}\n'
' network_mode: host\n'
' cap_add:\n'
' - NET_ADMIN\n'
' environment:\n'
' - SSHUTTLE_LISTEN_PORT=${REDIRECT_PORT}\n'
' volumes:\n'
' - ${PIC_DATA_DIR}/services/sshuttle/${INSTANCE_ID}/config:/config\n'
)
def _instanceable_manifest():
return {
'id': 'sshuttle',
'kind': 'store',
'instanceable': True,
'requires_host_network': True,
'config_schema': {},
}
class TestPerInstanceRender(unittest.TestCase):
def test_substitutes_instance_id_redirect_port_and_data_dir(self):
with tempfile.TemporaryDirectory() as tmpdir:
composer = ServiceComposer(config_manager=_make_cm(), data_dir=tmpdir)
content = composer.render_template(
'sshuttle', _instanceable_manifest(), _INSTANCEABLE_TEMPLATE,
instance_vars={'INSTANCE_ID': 'abcd1234', 'REDIRECT_PORT': '9101'})
self.assertIn('cell-sshuttle-abcd1234', content)
self.assertIn('SSHUTTLE_LISTEN_PORT=9101', content)
self.assertIn(
os.path.join(tmpdir, 'services', 'sshuttle', 'abcd1234', 'config'),
content)
self.assertNotIn('${INSTANCE_ID}', content)
self.assertNotIn('${REDIRECT_PORT}', content)
def test_two_instances_distinct_names_dirs_ports(self):
with tempfile.TemporaryDirectory() as tmpdir:
composer = ServiceComposer(config_manager=_make_cm(), data_dir=tmpdir)
manifest = _instanceable_manifest()
c1 = composer.write_instance_compose(
'sshuttle', 'aaaa1111', manifest, _INSTANCEABLE_TEMPLATE,
redirect_port=9101)
c2 = composer.write_instance_compose(
'sshuttle', 'bbbb2222', manifest, _INSTANCEABLE_TEMPLATE,
redirect_port=9102)
self.assertIn('cell-sshuttle-aaaa1111', c1)
self.assertIn('cell-sshuttle-bbbb2222', c2)
self.assertNotEqual(
composer._instance_compose_path('sshuttle', 'aaaa1111'),
composer._instance_compose_path('sshuttle', 'bbbb2222'))
self.assertIn('9101', c1)
self.assertIn('9102', c2)
self.assertTrue(os.path.exists(
composer._instance_compose_path('sshuttle', 'aaaa1111')))
self.assertTrue(os.path.exists(
composer.instance_config_dir('sshuttle', 'bbbb2222')))
@patch('service_composer.subprocess.run')
def test_up_instance_uses_unique_project_and_compose(self, mock_run):
mock_run.return_value = MagicMock(returncode=0, stdout='', stderr='')
with tempfile.TemporaryDirectory() as tmpdir:
composer = ServiceComposer(config_manager=_make_cm(), data_dir=tmpdir)
res = composer.up_instance(
'sshuttle', 'abcd1234', _instanceable_manifest(),
_INSTANCEABLE_TEMPLATE, redirect_port=9101)
self.assertTrue(res['ok'])
cmd = mock_run.call_args[0][0]
self.assertIn('--project-name', cmd)
self.assertIn('pic-conn-abcd1234', cmd)
self.assertIn('up', cmd)
@patch('service_composer.subprocess.run')
def test_down_instance_removes_dir(self, mock_run):
mock_run.return_value = MagicMock(returncode=0, stdout='', stderr='')
with tempfile.TemporaryDirectory() as tmpdir:
composer = ServiceComposer(config_manager=_make_cm(), data_dir=tmpdir)
composer.write_instance_compose(
'sshuttle', 'abcd1234', _instanceable_manifest(),
_INSTANCEABLE_TEMPLATE, redirect_port=9101)
inst_dir = composer._instance_dir('sshuttle', 'abcd1234')
self.assertTrue(os.path.isdir(inst_dir))
res = composer.down_instance('sshuttle', 'abcd1234', purge_data=True)
self.assertTrue(res['ok'])
self.assertFalse(os.path.exists(inst_dir))
def test_instance_id_for_strips_prefix(self):
self.assertEqual(ServiceComposer.instance_id_for('conn_a1b2c3d4'), 'a1b2c3d4')
if __name__ == '__main__':
unittest.main()