Files
wehub-resource-sync e0e362d700
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
Deploy Docs Pages / build (push) Has been cancelled
Deploy Docs Pages / deploy (push) Has been cancelled
Real E2E Tests / JavaScript E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Python E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Java E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / C# E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Go E2E (docker bridge) (push) Has been cancelled
Real E2E Tests / Real E2E CI (push) Has been cancelled
SDK Tests / SDK CI (push) Has been cancelled
SDK Tests / CLI Tests (push) Has been cancelled
SDK Tests / Python SDK Quality (code-interpreter) (push) Has been cancelled
SDK Tests / Python SDK Quality (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Python SDK Tests (sandbox) (push) Has been cancelled
SDK Tests / CLI Quality (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Has been cancelled
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Has been cancelled
SDK Tests / Go SDK Quality And Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

99 lines
3.5 KiB
Python

# Copyright 2026 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import random
import socket
from typing import Dict, Optional
from fastapi import HTTPException, status
from opensandbox_server.services.constants import SandboxErrorCodes
DOCKER_PUBLISH_HOST = "0.0.0.0"
# The probe is a short-lived availability check and must match Docker's
# publish scope; probing only localhost can miss ports bound on other host
# interfaces that Docker would later fail to publish.
PORT_PROBE_HOST = DOCKER_PUBLISH_HOST
def normalize_container_port_spec(port_spec: str) -> str:
token = str(port_spec).strip()
if token.endswith("/tcp"):
return token[:-4]
return token
def normalize_port_bindings(
port_bindings: dict[str, tuple[str, int]],
) -> dict[str, tuple[str, int]]:
"""
Normalize binding keys to docker-py canonical forms.
Docker port bindings accept "port" for tcp and "port/udp" for udp.
"""
normalized: dict[str, tuple[str, int]] = {}
for container_port, binding in port_bindings.items():
normalized_key = normalize_container_port_spec(container_port)
normalized[normalized_key] = binding
return normalized
def allocate_host_port(
min_port: int = 40000,
max_port: int = 60000,
attempts: int = 50,
) -> Optional[int]:
"""Find an available TCP port on the host within the given range."""
for _ in range(attempts):
port = random.randint(min_port, max_port)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
# This does not listen for or accept connections; it mirrors the
# later Docker publish binding to catch host-wide port conflicts.
# codeql[py/bind-socket-all-network-interfaces]
sock.bind((PORT_PROBE_HOST, port))
except OSError:
continue
return port
return None
def allocate_port_bindings(
container_ports: list[str],
min_port: int = 40000,
max_port: int = 60000,
) -> Dict[str, tuple[str, int]]:
"""Allocate distinct random host ports for each container port spec."""
allocated_ports: set[int] = set()
bindings: Dict[str, tuple[str, int]] = {}
for container_port in container_ports:
while True:
host_port = allocate_host_port(min_port=min_port, max_port=max_port)
if host_port is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail={
"code": SandboxErrorCodes.CONTAINER_START_FAILED,
"message": "Failed to allocate host ports for sandbox container.",
},
)
if host_port not in allocated_ports:
allocated_ports.add(host_port)
bindings[container_port] = (DOCKER_PUBLISH_HOST, host_port)
break
return bindings