Files
wehub-resource-sync e0e362d700
SDK Tests / SDK CI (push) Blocked by required conditions
SDK Tests / changes (push) Successful in 2m29s
Real E2E Tests / changes (push) Successful in 2m29s
SDK Tests / Python SDK Tests (sandbox) (push) Waiting to run
SDK Tests / CLI Quality (push) Waiting to run
SDK Tests / CLI Tests (push) Waiting to run
SDK Tests / Python SDK Quality (code-interpreter) (push) Waiting to run
SDK Tests / Python SDK Quality (sandbox) (push) Waiting to run
SDK Tests / Python SDK Tests (code-interpreter) (push) Waiting to run
SDK Tests / JavaScript SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / JavaScript SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Kotlin SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Kotlin SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / C# SDK Quality And Tests (code-interpreter) (push) Waiting to run
SDK Tests / C# SDK Quality And Tests (sandbox) (push) Waiting to run
SDK Tests / Go SDK Quality And Tests (push) Waiting to run
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
chore: import upstream snapshot with attribution
2026-07-13 13:39:33 +08:00

227 lines
7.7 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.
import unittest.mock
from types import SimpleNamespace
from unittest.mock import MagicMock
import pytest
from kubernetes.client.exceptions import ApiException
from opensandbox_server.config import AppConfig, EgressConfig, RuntimeConfig, SecureRuntimeConfig
from opensandbox_server.services.runtime_resolver import (
SecureRuntimeResolver,
validate_secure_runtime_on_startup,
)
def _config(runtime_type: str = "docker", secure_runtime=None, egress=None):
return AppConfig(
runtime=RuntimeConfig(type=runtime_type, execd_image="opensandbox/execd:test"),
secure_runtime=secure_runtime,
egress=egress,
)
def test_secure_runtime_resolver_disabled_without_runtime() -> None:
resolver = SecureRuntimeResolver(_config())
assert resolver.is_enabled() is False
assert resolver.get_docker_runtime() is None
assert resolver.get_k8s_runtime_class() is None
def test_secure_runtime_resolver_prefers_explicit_values_and_defaults() -> None:
docker_resolver = SecureRuntimeResolver(
_config(secure_runtime=SecureRuntimeConfig(type="gvisor", docker_runtime="custom-runsc"))
)
k8s_resolver = SecureRuntimeResolver(
_config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="kata", k8s_runtime_class="kata-custom"),
)
)
default_docker = SecureRuntimeResolver(
_config(secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"))
)
default_k8s = SecureRuntimeResolver(
_config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="kata", docker_runtime="kata-runtime"),
)
)
assert docker_resolver.is_enabled() is True
assert docker_resolver.get_docker_runtime() == "custom-runsc"
assert k8s_resolver.get_k8s_runtime_class() == "kata-custom"
assert default_docker.get_docker_runtime() == "runsc"
assert default_k8s.get_k8s_runtime_class() == "kata-qemu"
@pytest.mark.asyncio
async def test_validate_secure_runtime_skips_when_disabled() -> None:
docker_client = MagicMock()
await validate_secure_runtime_on_startup(_config(), docker_client=docker_client)
docker_client.info.assert_not_called()
@pytest.mark.asyncio
async def test_validate_secure_runtime_checks_docker_runtime() -> None:
docker_client = MagicMock()
docker_client.info.return_value = {"Runtimes": {"runsc": {"path": "/usr/bin/runsc"}}}
config = _config(
secure_runtime=SecureRuntimeConfig(type="gvisor", docker_runtime="runsc")
)
await validate_secure_runtime_on_startup(config, docker_client=docker_client)
docker_client.info.assert_called_once()
@pytest.mark.asyncio
async def test_validate_secure_runtime_rejects_missing_docker_runtime() -> None:
docker_client = MagicMock()
docker_client.info.return_value = {"Runtimes": {"runc": {}}}
config = _config(
secure_runtime=SecureRuntimeConfig(type="gvisor", docker_runtime="runsc")
)
with pytest.raises(ValueError, match="runsc"):
await validate_secure_runtime_on_startup(config, docker_client=docker_client)
@pytest.mark.asyncio
async def test_validate_secure_runtime_allows_missing_docker_client() -> None:
config = _config(
secure_runtime=SecureRuntimeConfig(type="gvisor", docker_runtime="runsc")
)
await validate_secure_runtime_on_startup(config, docker_client=None)
@pytest.mark.asyncio
async def test_validate_secure_runtime_checks_k8s_runtime_class() -> None:
k8s_client = MagicMock()
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"),
)
await validate_secure_runtime_on_startup(config, k8s_client=k8s_client)
k8s_client.read_runtime_class.assert_called_once_with("gvisor")
@pytest.mark.asyncio
async def test_validate_secure_runtime_rejects_missing_k8s_runtime_class() -> None:
k8s_client = MagicMock()
k8s_client.read_runtime_class.side_effect = ApiException(status=404)
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"),
)
with pytest.raises(ValueError, match="RuntimeClass 'gvisor'"):
await validate_secure_runtime_on_startup(config, k8s_client=k8s_client)
@pytest.mark.asyncio
async def test_validate_secure_runtime_reraises_k8s_api_errors() -> None:
k8s_client = MagicMock()
k8s_client.read_runtime_class.side_effect = ApiException(status=500)
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"),
)
with pytest.raises(ApiException):
await validate_secure_runtime_on_startup(config, k8s_client=k8s_client)
@pytest.mark.asyncio
async def test_validate_secure_runtime_allows_missing_k8s_client() -> None:
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"),
)
await validate_secure_runtime_on_startup(config, k8s_client=None)
@pytest.mark.asyncio
async def test_validate_secure_runtime_skips_unknown_runtime_type() -> None:
config = SimpleNamespace(
runtime=SimpleNamespace(type="custom"),
secure_runtime=SimpleNamespace(
type="gvisor",
docker_runtime="runsc",
k8s_runtime_class=None,
),
)
await validate_secure_runtime_on_startup(config)
@pytest.mark.asyncio
async def test_validate_startup_warns_gvisor_with_egress() -> None:
k8s_client = MagicMock()
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"),
egress=EgressConfig(image="opensandbox/egress:latest"),
)
with unittest.mock.patch(
"opensandbox_server.services.runtime_resolver.logger"
) as mock_logger:
await validate_secure_runtime_on_startup(config, k8s_client=k8s_client)
mock_logger.warning.assert_called_once()
assert "iptables nat" in mock_logger.warning.call_args[0][0]
@pytest.mark.asyncio
async def test_validate_startup_no_warn_gvisor_without_egress() -> None:
k8s_client = MagicMock()
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="gvisor", k8s_runtime_class="gvisor"),
)
with unittest.mock.patch(
"opensandbox_server.services.runtime_resolver.logger"
) as mock_logger:
await validate_secure_runtime_on_startup(config, k8s_client=k8s_client)
mock_logger.warning.assert_not_called()
@pytest.mark.asyncio
async def test_validate_startup_no_warn_kata_with_egress() -> None:
k8s_client = MagicMock()
config = _config(
runtime_type="kubernetes",
secure_runtime=SecureRuntimeConfig(type="kata", k8s_runtime_class="kata-qemu"),
egress=EgressConfig(image="opensandbox/egress:latest"),
)
with unittest.mock.patch(
"opensandbox_server.services.runtime_resolver.logger"
) as mock_logger:
await validate_secure_runtime_on_startup(config, k8s_client=k8s_client)
mock_logger.warning.assert_not_called()