Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion services/core/tools/e2b-provider/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ An unknown Create is never repeated. A Create whose connection material was lost

## Inspection and cleanup

Inspection uses SDK metadata and ID reads only. SDK `connect` is never used because it can resume paused compute. The version-pinned constructor that restores a client from saved connection material is confined to [`sdk.py`](sdk.py) and covered by a no-connect, no-create test. Resource drift fails inspection but still permits ownership-based cleanup.
Inspection accepts an omitted `SandboxInfo.sandbox_domain`, as allowed by the pinned SDK; an explicit domain must match the configured domain or its subdomains. Create connection objects and clients restored from saved connection material must always have a matching domain before envd access. Ownership, template and resource checks apply regardless of the observation domain. Inspection uses SDK metadata and ID reads only. SDK `connect` is never used because it can resume paused compute. The version-pinned constructor that restores a client from saved connection material is confined to [`sdk.py`](sdk.py) and covered by a no-connect, no-create test. Resource drift fails inspection but still permits ownership-based cleanup.

`CreateSettled` proves that the original Create and bootstrap can no longer mutate; it is independent of `BootstrapComplete`. An empty lookup never settles an unknown Create. Kill destroys every matching sandbox, confirms that none remains and only then records a settled tombstone; it returns `State=absent` with `CreateSettled`. A settled rejected Create with no sandbox IDs proves absence without a cloud request, so GetInfo and Kill still succeed when the key is invalid. Ordinary missing compute has no such proof.

Expand Down
118 changes: 118 additions & 0 deletions services/core/tools/e2b-provider/observation_domain_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""Optional SDK observation metadata never relaxes connection validation."""
from pathlib import Path
from types import SimpleNamespace
import unittest

import provider_test


class ObservationDomainTest(unittest.TestCase):
setUp = provider_test.ProviderTest.setUp
call = provider_test.ProviderTest.call
record = provider_test.ProviderTest.record

def observed(self, **changes):
fields = dict(sandbox_id=self.cloud.sandbox_id, sandbox_domain=None,
metadata=self.cloud.metadata, template_id=self.cloud.template_id,
cpu_count=2, memory_mb=2048, state=self.cloud.state)
fields.update(changes)
return SimpleNamespace(**fields)

def test_create_and_inspect_missing_observation_domain_use_saved_connection(self):
self.config['Resources'] = {'cpus': 2, 'memory_mib': 2048}
self.api.get_info.side_effect = lambda *a, **k: self.observed()
self.assertEqual(self.call('create')['ErrorCode'], '')
self.assertTrue(self.call('inspect')['Info']['BootstrapComplete'])
self.assertEqual(self.call('renew')['ErrorCode'], '')
self.assertEqual(self.record()['connection']['sandbox_domain'], 'e2b.app')
self.api.connect.assert_not_called()
self.api.create.assert_called_once()

def test_paused_inspection_without_domain_does_not_wake(self):
self.api.get_info.side_effect = lambda *a, **k: self.observed()
self.assertEqual(self.call('create')['ErrorCode'], '')
self.cloud.state = 'paused'
result = self.call('inspect')
self.assertEqual(result['ErrorCode'], '')
self.assertEqual(result['Info']['State'], 'paused')
self.api.connect.assert_not_called()

def test_unknown_create_list_without_domain_never_recreates_or_attaches(self):
self.api.create.side_effect = TimeoutError('unknown create')
self.assertEqual(self.call('create')['ErrorCode'], 'unconfirmed')
self.api.list.return_value = SimpleNamespace(
has_next=True, next_items=lambda **kwargs: [self.observed()])
# One page exhausts the SDK paginator.
def page(**kwargs):
self.api.list.return_value.has_next = False
return [self.observed()]
self.api.list.return_value.next_items = page
result = self.call('inspect')
self.assertEqual(result['ErrorCode'], '')
self.assertFalse(result['Info']['BootstrapComplete'])
self.api.create.assert_called_once()
self.api.connect.assert_not_called()
self.cloud.files.write.assert_not_called()

def test_missing_domain_does_not_bypass_identity_or_resource_checks(self):
self.config['Resources'] = {'cpus': 2, 'memory_mib': 2048}
for changes, error in (({'metadata': {}}, 'ownership'),
({'template_id': 'other'}, 'invalid'),
({'cpu_count': 4}, 'invalid'),
({'memory_mb': 8192}, 'invalid')):
with self.subTest(changes=changes):
for path in Path(self.temporary.name).glob('*.json'):
path.unlink()
self.api.get_info.return_value = self.observed(**changes)
self.assertEqual(self.call('create')['ErrorCode'], error)
self.cloud.files.write.assert_not_called()
self.api.connect.assert_not_called()

def test_explicit_invalid_observation_domain_rejected_before_credentials(self):
for domain in ('foreign.example', 'e2b.app.foreign.example', '', 3):
with self.subTest(domain=domain):
for path in Path(self.temporary.name).glob('*.json'):
path.unlink()
self.api.get_info.return_value = self.observed(sandbox_domain=domain)
self.assertEqual(self.call('create')['ErrorCode'], 'ownership')
self.cloud.files.write.assert_not_called()
self.api.connect.assert_not_called()

def test_rejected_connection_cannot_reenter_through_missing_observation_domain(self):
for domain in ('foreign.example', None):
with self.subTest(domain=domain):
for path in Path(self.temporary.name).glob('*.json'):
path.unlink()
self.cloud.sandbox_domain = domain
self.api.get_info.return_value = self.observed()
self.assertEqual(self.call('create')['ErrorCode'], 'ownership')
self.assertEqual(self.call('inspect')['ErrorCode'], 'ownership')
self.cloud.files.read.assert_not_called()
self.cloud.files.write.assert_not_called()
self.api.connect.assert_not_called()

def test_rejected_observation_cannot_resume_bootstrap_on_later_missing_domain(self):
self.api.get_info.return_value = self.observed(sandbox_domain='foreign.example')
self.assertEqual(self.call('create')['ErrorCode'], 'ownership')
self.assertEqual(self.record()['status'], 'configuration_rejected')
self.api.get_info.return_value = self.observed()
result = self.call('inspect')
self.assertEqual(result['ErrorCode'], '')
self.assertFalse(result['Info']['BootstrapComplete'])
self.assertTrue(result['Info']['CreateSettled'])
self.assertEqual(self.record()['status'], 'configuration_rejected')
self.cloud.files.read.assert_not_called()
self.cloud.files.write.assert_not_called()
self.api.connect.assert_not_called()

def test_missing_actual_connection_domain_is_still_rejected(self):
self.api.get_info.return_value = self.observed()
self.cloud.sandbox_domain = None
self.assertEqual(self.call('create')['ErrorCode'], 'ownership')
self.api.get_info.assert_not_called()
self.cloud.files.write.assert_not_called()
self.api.connect.assert_not_called()


if __name__ == '__main__':
unittest.main()
14 changes: 9 additions & 5 deletions services/core/tools/e2b-provider/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,18 @@ def owns(self, cloud):
raise Failure('ownership')
return cloud

def check_domain(self, cloud):
def check_domain(self, sandbox_domain):
domain = self.options()['domain']
sandbox_domain = cloud.sandbox_domain
if not isinstance(sandbox_domain, str) or not (
sandbox_domain == domain or sandbox_domain.endswith('.' + domain)):
raise Failure('ownership')

def qualified(self, cloud):
self.check_domain(cloud)
# SandboxInfo omits the optional domain on the official API. It is an
# observation, not connection material; Create and saved clients still
# require a qualified domain before any envd access.
if cloud.sandbox_domain is not None:
self.check_domain(cloud.sandbox_domain)
template = self.config['Template'].split(':', 1)[0]
if cloud.template_id not in (template, self.config['Template']):
raise Failure('invalid')
Expand Down Expand Up @@ -173,6 +176,7 @@ def client(self, cloud):
material = (self.receipt.data or {}).get('connection')
if not material or material['sandbox_id'] != cloud.sandbox_id:
raise Failure('unconfirmed')
self.check_domain(material['sandbox_domain'])
return restore(material, self.options())

def inspect(self):
Expand All @@ -189,7 +193,7 @@ def inspect(self):
self.qualified(cloud)
record = self.receipt.data or {}
if (cloud.state == 'running' and not record.get('bootstrap_complete') and record.get('connection')
and record.get('status') not in ('bootstrap_failed', 'killed')):
and record.get('status') not in ('configuration_rejected', 'bootstrap_failed', 'killed')):
try:
receipt = json.loads(self.client(cloud).files.read('/root/.oac/e2b/managed-ready.json',
user='root', request_timeout=self.remaining()))
Expand Down Expand Up @@ -222,7 +226,7 @@ def create(self):
raise Failure('unconfirmed') from None
self.receipt.save(status='created', ids=[cloud.sandbox_id], connection=connection_material(cloud))
# A create response must not steer envd traffic to an unrelated host.
self.check_domain(cloud)
self.check_domain(cloud.sandbox_domain)
# SDK Create returns connection material, but no metadata or resources.
# Read its exact ID before writing credentials, even when Core adopts the
# template's resources and does not supply explicit limits.
Expand Down
Loading