Skip to content

Commit 4c28fab

Browse files
authored
Fixes bug in sandbox_client that assumed pod name matched the sandbox name (#150)
* Fixes bug in sandbox_client * Removes unnecessary sandboxclaim watch * Addresses review comments * Rebase to main * Adds tests for the pod name
1 parent a9d9555 commit 4c28fab

File tree

2 files changed

+69
-29
lines changed

2 files changed

+69
-29
lines changed

clients/python/agentic-sandbox-client/agentic_sandbox/sandbox_client.py

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
SANDBOX_API_VERSION = "v1alpha1"
3939
SANDBOX_PLURAL_NAME = "sandboxes"
4040

41+
POD_NAME_ANNOTATION = "agents.x-k8s.io/pod-name"
42+
4143
logging.basicConfig(level=logging.INFO,
4244
format='%(asctime)s - %(levelname)s - %(message)s',
4345
stream=sys.stdout)
@@ -82,6 +84,8 @@ def __init__(
8284

8385
self.claim_name: str | None = None
8486
self.sandbox_name: str | None = None
87+
self.pod_name: str | None = None
88+
self.annotations: dict | None = None
8589

8690
try:
8791
config.load_incluster_config()
@@ -145,25 +149,41 @@ def _wait_for_sandbox_ready(self):
145149
field_selector=f"metadata.name={self.claim_name}",
146150
timeout_seconds=self.sandbox_ready_timeout
147151
):
148-
sandbox_object = event['object']
149-
status = sandbox_object.get('status', {})
150-
conditions = status.get('conditions', [])
151-
is_ready = False
152-
for cond in conditions:
153-
if cond.get('type') == 'Ready' and cond.get('status') == 'True':
154-
is_ready = True
155-
break
156-
157-
if is_ready:
158-
self.sandbox_name = sandbox_object['metadata']['name']
159-
w.stop()
160-
logging.info(f"Sandbox {self.sandbox_name} is ready.")
161-
break
162-
163-
if not self.sandbox_name:
164-
self.__exit__(None, None, None)
165-
raise TimeoutError(
166-
f"Sandbox did not become ready within {self.sandbox_ready_timeout} seconds.")
152+
if event["type"] in ["ADDED", "MODIFIED"]:
153+
sandbox_object = event['object']
154+
status = sandbox_object.get('status', {})
155+
conditions = status.get('conditions', [])
156+
is_ready = False
157+
for cond in conditions:
158+
if cond.get('type') == 'Ready' and cond.get('status') == 'True':
159+
is_ready = True
160+
break
161+
162+
if is_ready:
163+
metadata = sandbox_object.get(
164+
"metadata", {})
165+
self.sandbox_name = metadata.get(
166+
"name")
167+
if not self.sandbox_name:
168+
raise RuntimeError(
169+
"Could not determine sandbox name from sandbox object.")
170+
logging.info(f"Sandbox {self.sandbox_name} is ready.")
171+
172+
self.annotations = sandbox_object.get(
173+
'metadata', {}).get('annotations', {})
174+
pod_name = self.annotations.get(POD_NAME_ANNOTATION)
175+
if pod_name:
176+
self.pod_name = pod_name
177+
logging.info(
178+
f"Found pod name from annotation: {self.pod_name}")
179+
else:
180+
self.pod_name = self.sandbox_name
181+
w.stop()
182+
return
183+
184+
self.__exit__(None, None, None)
185+
raise TimeoutError(
186+
f"Sandbox did not become ready within {self.sandbox_ready_timeout} seconds.")
167187

168188
def _get_free_port(self):
169189
"""Finds a free port on localhost."""

clients/python/agentic-sandbox-client/test_client.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
import asyncio
1717
from agentic_sandbox import SandboxClient
1818

19+
POD_NAME_ANNOTATION = "agents.x-k8s.io/pod-name"
20+
1921

2022
async def main(template_name: str, gateway_name: str | None, api_url: str | None, namespace: str, server_port: int):
2123
"""
2224
Tests the Sandbox client by creating a sandbox, running a command,
2325
and then cleaning up.
2426
"""
2527

26-
print(f"--- Starting Sandbox Client Test (Namespace: {namespace}, Port: {server_port}) ---")
28+
print(
29+
f"--- Starting Sandbox Client Test (Namespace: {namespace}, Port: {server_port}) ---")
2730
if gateway_name:
2831
print(f"Mode: Gateway Discovery ({gateway_name})")
2932
elif api_url:
@@ -41,6 +44,20 @@ async def main(template_name: str, gateway_name: str | None, api_url: str | None
4144
server_port=server_port
4245
) as sandbox:
4346

47+
print("\n--- Testing Pod Name Discovery ---")
48+
assert sandbox.annotations is not None, "Sandbox annotations were not stored on the client"
49+
50+
pod_name_annotation = sandbox.annotations.get(POD_NAME_ANNOTATION)
51+
52+
if pod_name_annotation:
53+
print(f"Found pod name from annotation: {pod_name_annotation}")
54+
assert sandbox.pod_name == pod_name_annotation, f"Expected pod_name to be '{pod_name_annotation}', but got '{sandbox.pod_name}'"
55+
print("--- Pod Name Discovery Test Passed (Annotation) ---")
56+
else:
57+
print("Pod name annotation not found, falling back to sandbox name.")
58+
assert sandbox.pod_name == sandbox.sandbox_name, f"Expected pod_name to be '{sandbox.sandbox_name}', but got '{sandbox.pod_name}'"
59+
print("--- Pod Name Discovery Test Passed (Fallback) ---")
60+
4461
print("\n--- Testing Command Execution ---")
4562
command_to_run = "echo 'Hello from the sandbox!'"
4663
print(f"Executing command: '{command_to_run}'")
@@ -97,24 +114,27 @@ async def main(template_name: str, gateway_name: str | None, api_url: str | None
97114
default="python-sandbox-template",
98115
help="The name of the sandbox template to use for the test."
99116
)
100-
117+
101118
# Default is None to allow testing the Port-Forward fallback
102119
parser.add_argument(
103120
"--gateway-name",
104-
default=None,
121+
default=None,
105122
help="The name of the Gateway resource. If omitted, defaults to local port-forward mode."
106123
)
107-
108-
parser.add_argument("--api-url", help="Direct URL to router (e.g. http://localhost:8080)", default=None)
109-
parser.add_argument("--namespace", default="default", help="Namespace to create sandbox in")
110-
parser.add_argument("--server-port", type=int, default=8888, help="Port the sandbox container listens on")
111-
124+
125+
parser.add_argument(
126+
"--api-url", help="Direct URL to router (e.g. http://localhost:8080)", default=None)
127+
parser.add_argument("--namespace", default="default",
128+
help="Namespace to create sandbox in")
129+
parser.add_argument("--server-port", type=int, default=8888,
130+
help="Port the sandbox container listens on")
131+
112132
args = parser.parse_args()
113-
133+
114134
asyncio.run(main(
115135
template_name=args.template_name,
116136
gateway_name=args.gateway_name,
117137
api_url=args.api_url,
118138
namespace=args.namespace,
119139
server_port=args.server_port
120-
))
140+
))

0 commit comments

Comments
 (0)