Merge feature/add_r_execenv: Add R execution support and refactor pod_executor imports
This commit is contained in:
commit
403b48bc8e
21 changed files with 120 additions and 86 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
MCP-Forge is an MCP (Model Context Protocol) server that provides intelligent code execution capabilities for AI agents. It enables agents to execute Python code in isolated containers while having access to other MCP tools, optimizing data processing by keeping large datasets in the execution environment rather than passing them through the LLM context.
|
MCP-Forge is an MCP (Model Context Protocol) server that provides intelligent code execution capabilities for AI agents. It enables agents to execute Python and R code in isolated containers while having access to other MCP tools, optimizing data processing by keeping large datasets in the execution environment rather than passing them through the LLM context.
|
||||||
|
|
||||||
## Core Concept
|
## Core Concept
|
||||||
|
|
||||||
|
|
@ -12,7 +12,7 @@ MCP-Forge is an MCP (Model Context Protocol) server that provides intelligent co
|
||||||
- Limited by context window size
|
- Limited by context window size
|
||||||
|
|
||||||
**Solution:** MCP-Forge provides a code execution environment where:
|
**Solution:** MCP-Forge provides a code execution environment where:
|
||||||
- MCP tools are available as Python functions
|
- MCP tools are available as language-native functions (Python and R)
|
||||||
- Agents can write code to process data locally
|
- Agents can write code to process data locally
|
||||||
- Only relevant results flow back to the agent
|
- Only relevant results flow back to the agent
|
||||||
- The agent already knows tool signatures from its own configuration
|
- The agent already knows tool signatures from its own configuration
|
||||||
|
|
@ -58,7 +58,7 @@ MCP-Forge Server
|
||||||
│ └─ mcp-forge/custom:{user-env-name}
|
│ └─ mcp-forge/custom:{user-env-name}
|
||||||
│
|
│
|
||||||
└─ Isolated Execution Containers
|
└─ Isolated Execution Containers
|
||||||
├─ Python Runtime
|
├─ Python/R Runtime
|
||||||
├─ MCP Client (injected tools)
|
├─ MCP Client (injected tools)
|
||||||
└─ Session Volumes
|
└─ Session Volumes
|
||||||
```
|
```
|
||||||
|
|
@ -137,17 +137,19 @@ MCP-Forge Server
|
||||||
|
|
||||||
#### Tools
|
#### Tools
|
||||||
|
|
||||||
**`execute_python`**
|
**`execute_code`**
|
||||||
|
|
||||||
Execute Python code in an isolated container with MCP tools available.
|
Execute Python or R code in an isolated container with MCP tools available.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"code": "string (required) - Python code to execute",
|
"code": "string (required) - Source code to execute",
|
||||||
|
"language": "string (optional) - 'python' (default) or 'r'",
|
||||||
"mcp_tools": "array (optional) - List of MCP tool names to inject",
|
"mcp_tools": "array (optional) - List of MCP tool names to inject",
|
||||||
"session_id": "string (optional) - Session ID for stateful execution (null = stateless)",
|
"session_id": "string (optional) - Session ID for stateful execution (null = stateless)",
|
||||||
"backend": "string (optional) - 'simple' (default) or 'jupyter'",
|
"backend": "string (optional) - 'simple' (default) or 'jupyter'",
|
||||||
|
"kernel": "string (optional) - for jupyter backend: 'python' (ipykernel) or 'r' (rkernel)",
|
||||||
"timeout": "integer (optional) - Max execution time in seconds (default: 300)",
|
"timeout": "integer (optional) - Max execution time in seconds (default: 300)",
|
||||||
"volumes": "object (optional) - Volume mount configuration",
|
"volumes": "object (optional) - Volume mount configuration",
|
||||||
"custom_image": "string (optional) - Custom environment name to use",
|
"custom_image": "string (optional) - Custom environment name to use",
|
||||||
|
|
@ -1446,3 +1448,28 @@ print(result['result'])
|
||||||
- Comprehensive monitoring
|
- Comprehensive monitoring
|
||||||
- High availability setup
|
- High availability setup
|
||||||
- Documentation and examples
|
- Documentation and examples
|
||||||
|
|
||||||
|
## Multi-language Execution (Python + R)
|
||||||
|
|
||||||
|
MCP-Forge supports both Python and R execution modes:
|
||||||
|
|
||||||
|
- **Simple backend**
|
||||||
|
- `language=python` runs python shell execution (existing path)
|
||||||
|
- `language=r` runs R shell execution (new path)
|
||||||
|
- Stateless by default
|
||||||
|
|
||||||
|
- **Jupyter backend**
|
||||||
|
- `kernel=python` uses IPython kernel (existing path)
|
||||||
|
- `kernel=r` uses IRkernel (new path)
|
||||||
|
- Stateful sessions keyed by `session_id`
|
||||||
|
|
||||||
|
### Tool Injection for R
|
||||||
|
|
||||||
|
The MCP bridge protocol remains Unix-socket JSON based and language-agnostic.
|
||||||
|
R injection wrappers mirror Python wrappers at a behavioral level:
|
||||||
|
|
||||||
|
- Generate R wrapper functions per allowed MCP tool
|
||||||
|
- Forward tool calls over mounted bridge socket
|
||||||
|
- Decode JSON responses to R lists/vectors
|
||||||
|
|
||||||
|
This keeps execution containers air-gapped while preserving access to configured MCP tools.
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,13 @@
|
||||||
- Phase 5.3: Integration & End-to-End Testing
|
- Phase 5.3: Integration & End-to-End Testing
|
||||||
- Phase 6: Documentation & Deployment
|
- Phase 6: Documentation & Deployment
|
||||||
|
|
||||||
|
**New Workstream (2026-03-04): R execution environments**
|
||||||
|
- Add language selection to execution tooling (`python` + `r`)
|
||||||
|
- Add simple backend R shell execution path
|
||||||
|
- Add stateful Jupyter R kernel support (`IRkernel`)
|
||||||
|
- Add MCP tool injection generator for R wrappers
|
||||||
|
- Add tests first for R simple/stateful execution and server dispatch
|
||||||
|
|
||||||
**Test Count:** 387 tests passing
|
**Test Count:** 387 tests passing
|
||||||
|
|
||||||
**Last Updated:** 2026-02-06
|
**Last Updated:** 2026-02-06
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ from typing import Optional
|
||||||
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
||||||
|
|
||||||
from mcp_forge.config.loader import load_config
|
from mcp_forge.config.loader import load_config
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import AllowlistValidator
|
from mcp_forge.security.allowlist import AllowlistValidator
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
from mcp_forge.execution.simple.backend import SimpleBackend
|
from mcp_forge.adapters import SimpleBackend
|
||||||
from mcp_forge.execution.jupyter.backend import JupyterBackend
|
from mcp_forge.execution.jupyter.backend import JupyterBackend
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,13 @@ from pathlib import Path
|
||||||
# Add src to path
|
# Add src to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
||||||
|
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.podman.containers import SecureContainerManager, ContainerConfig
|
from mcp_forge.podman.containers import SecureContainerManager, ContainerConfig
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
import time
|
import time
|
||||||
from typing import Optional, Dict
|
from typing import Optional, Dict
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import hashlib
|
||||||
from mcp_forge.config.schema import ForgeConfig
|
from mcp_forge.config.schema import ForgeConfig
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.audit import AuditLogger, AuditEventType, AuditSeverity
|
from mcp_forge.security.audit import AuditLogger, AuditEventType, AuditSeverity
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits, parse_memory_string
|
from pod_executor.security.resource_limits import ResourceLimits, parse_memory_string
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
||||||
from mcp_forge.execution.jupyter.sessions import SessionManager, SessionState, SessionError
|
from mcp_forge.execution.jupyter.sessions import SessionManager, SessionState, SessionError
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ from jupyter_client.blocking.client import BlockingKernelClient
|
||||||
import zmq
|
import zmq
|
||||||
|
|
||||||
from mcp_forge.podman.containers import SecureContainerManager, ContainerConfig
|
from mcp_forge.podman.containers import SecureContainerManager, ContainerConfig
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
|
|
||||||
|
|
||||||
class KernelError(Exception):
|
class KernelError(Exception):
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ from datetime import datetime, timedelta
|
||||||
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
||||||
from mcp_forge.config.schema import SessionConfig
|
from mcp_forge.config.schema import SessionConfig
|
||||||
from mcp_forge.security.audit import AuditLogger, AuditEventType, AuditSeverity
|
from mcp_forge.security.audit import AuditLogger, AuditEventType, AuditSeverity
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
|
|
||||||
|
|
||||||
class SessionError(Exception):
|
class SessionError(Exception):
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ from typing import Optional, Dict, List
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
||||||
from mcp_forge.security.audit import AuditLogger, AuditEventType, AuditSeverity
|
from mcp_forge.security.audit import AuditLogger, AuditEventType, AuditSeverity
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
|
|
||||||
class ContainerConfig:
|
class ContainerConfig:
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ from pathlib import Path
|
||||||
# Add src to path
|
# Add src to path
|
||||||
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
||||||
|
|
||||||
from mcp_forge.execution.simple.executor import CodeExecutor
|
from pod_executor.simple.executor import CodeExecutor
|
||||||
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator, AllowlistManager
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
|
|
||||||
|
|
||||||
class PassthroughValidator(OperationValidator):
|
class PassthroughValidator(OperationValidator):
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ from mcp_forge.builder.environment_builder import (
|
||||||
from mcp_forge.builder.package_validator import SecurityError
|
from mcp_forge.builder.package_validator import SecurityError
|
||||||
from mcp_forge.builder.image_builder import BuildResult
|
from mcp_forge.builder.image_builder import BuildResult
|
||||||
from mcp_forge.config.schema import EnvironmentBuilderConfig
|
from mcp_forge.config.schema import EnvironmentBuilderConfig
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from pathlib import Path
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from mcp_forge.builder.image_builder import ImageBuilder, BuildResult
|
from mcp_forge.builder.image_builder import ImageBuilder, BuildResult
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.config.schema import EnvironmentBuilderConfig
|
from mcp_forge.config.schema import EnvironmentBuilderConfig
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ from pathlib import Path
|
||||||
|
|
||||||
from mcp_forge.execution.jupyter.backend import JupyterBackend
|
from mcp_forge.execution.jupyter.backend import JupyterBackend
|
||||||
from mcp_forge.execution.jupyter.sessions import SessionManager, Session, SessionState
|
from mcp_forge.execution.jupyter.sessions import SessionManager, Session, SessionState
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
from mcp_forge.config.schema import ForgeConfig, ExecutionConfig, ImageConfig, SessionConfig
|
from mcp_forge.config.schema import ForgeConfig, ExecutionConfig, ImageConfig, SessionConfig
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ from mcp_forge.execution.jupyter.kernel import (
|
||||||
KernelError
|
KernelError
|
||||||
)
|
)
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ from mcp_forge.execution.jupyter.sessions import (
|
||||||
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
from mcp_forge.execution.jupyter.kernel import JupyterKernelManager
|
||||||
from mcp_forge.config.schema import SessionConfig
|
from mcp_forge.config.schema import SessionConfig
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ import pytest
|
||||||
from unittest.mock import Mock, MagicMock, patch
|
from unittest.mock import Mock, MagicMock, patch
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from mcp_forge.execution.simple.backend import SimpleBackend
|
from mcp_forge.adapters import SimpleBackend
|
||||||
from mcp_forge.execution.simple.executor import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
from mcp_forge.config.schema import ForgeConfig, ExecutionConfig, ImageConfig
|
from mcp_forge.config.schema import ForgeConfig, ExecutionConfig, ImageConfig
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import pytest
|
||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from mcp_forge.execution.simple.executor import CodeExecutor, ExecutionResult
|
from pod_executor.simple.executor import CodeExecutor, ExecutionResult
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ from pathlib import Path
|
||||||
def test_create_container_with_valid_params_succeeds():
|
def test_create_container_with_valid_params_succeeds():
|
||||||
"""Test that container creation with valid params succeeds."""
|
"""Test that container creation with valid params succeeds."""
|
||||||
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger, AuditEventType
|
from mcp_forge.security.audit import AuditLogger, AuditEventType
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
||||||
# Setup mocks
|
# Setup mocks
|
||||||
|
|
@ -51,7 +51,7 @@ def test_create_container_with_valid_params_succeeds():
|
||||||
def test_create_container_with_forbidden_params_raises_security_error():
|
def test_create_container_with_forbidden_params_raises_security_error():
|
||||||
"""Test that forbidden parameters raise SecurityError."""
|
"""Test that forbidden parameters raise SecurityError."""
|
||||||
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -80,7 +80,7 @@ def test_create_container_with_forbidden_params_raises_security_error():
|
||||||
def test_create_container_with_invalid_image_raises_security_error():
|
def test_create_container_with_invalid_image_raises_security_error():
|
||||||
"""Test that invalid/disallowed images raise SecurityError."""
|
"""Test that invalid/disallowed images raise SecurityError."""
|
||||||
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -126,7 +126,7 @@ def test_create_container_enforces_required_parameters():
|
||||||
def test_resource_limits_are_applied_correctly():
|
def test_resource_limits_are_applied_correctly():
|
||||||
"""Test that resource limits are correctly applied."""
|
"""Test that resource limits are correctly applied."""
|
||||||
from mcp_forge.podman.containers import ContainerConfig
|
from mcp_forge.podman.containers import ContainerConfig
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
limits = ResourceLimits(
|
limits = ResourceLimits(
|
||||||
memory="1g",
|
memory="1g",
|
||||||
|
|
@ -149,7 +149,7 @@ def test_resource_limits_are_applied_correctly():
|
||||||
def test_volume_mounts_are_validated():
|
def test_volume_mounts_are_validated():
|
||||||
"""Test that volume mounts are validated against allowlist."""
|
"""Test that volume mounts are validated against allowlist."""
|
||||||
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
from mcp_forge.podman.containers import ContainerConfig, SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -190,7 +190,7 @@ def test_volume_mounts_are_validated():
|
||||||
def test_start_container_on_session_container_succeeds():
|
def test_start_container_on_session_container_succeeds():
|
||||||
"""Test that starting a session container succeeds."""
|
"""Test that starting a session container succeeds."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -218,7 +218,7 @@ def test_start_container_on_session_container_succeeds():
|
||||||
def test_start_container_on_non_session_container_raises_security_error():
|
def test_start_container_on_non_session_container_raises_security_error():
|
||||||
"""Test that starting a non-session container raises SecurityError."""
|
"""Test that starting a non-session container raises SecurityError."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
from mcp_forge.security.allowlist import OperationValidator, SecurityError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -241,7 +241,7 @@ def test_start_container_on_non_session_container_raises_security_error():
|
||||||
def test_stop_container_works():
|
def test_stop_container_works():
|
||||||
"""Test that stopping a container works."""
|
"""Test that stopping a container works."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -269,7 +269,7 @@ def test_stop_container_works():
|
||||||
def test_remove_container_works():
|
def test_remove_container_works():
|
||||||
"""Test that removing a container works."""
|
"""Test that removing a container works."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -297,7 +297,7 @@ def test_remove_container_works():
|
||||||
def test_cleanup_old_containers():
|
def test_cleanup_old_containers():
|
||||||
"""Test cleanup of old containers."""
|
"""Test cleanup of old containers."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -340,7 +340,7 @@ def test_cleanup_old_containers():
|
||||||
def test_get_container_logs():
|
def test_get_container_logs():
|
||||||
"""Test getting container logs."""
|
"""Test getting container logs."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -371,7 +371,7 @@ def test_get_container_logs():
|
||||||
def test_wait_for_container():
|
def test_wait_for_container():
|
||||||
"""Test waiting for container to exit."""
|
"""Test waiting for container to exit."""
|
||||||
from mcp_forge.podman.containers import SecureContainerManager
|
from mcp_forge.podman.containers import SecureContainerManager
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -402,7 +402,7 @@ def test_wait_for_container():
|
||||||
def test_container_config_to_podman_params_includes_all_security_settings():
|
def test_container_config_to_podman_params_includes_all_security_settings():
|
||||||
"""Test that ContainerConfig.to_podman_params includes all required settings."""
|
"""Test that ContainerConfig.to_podman_params includes all required settings."""
|
||||||
from mcp_forge.podman.containers import ContainerConfig
|
from mcp_forge.podman.containers import ContainerConfig
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
config = ContainerConfig(
|
config = ContainerConfig(
|
||||||
image="mcp-forge/python:3.11",
|
image="mcp-forge/python:3.11",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from unittest.mock import Mock, MagicMock, patch
|
||||||
|
|
||||||
def test_connection_to_podman_socket_succeeds(tmp_path):
|
def test_connection_to_podman_socket_succeeds(tmp_path):
|
||||||
"""Test that connection to Podman socket succeeds."""
|
"""Test that connection to Podman socket succeeds."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -42,7 +42,7 @@ def test_connection_to_podman_socket_succeeds(tmp_path):
|
||||||
|
|
||||||
def test_connection_failure_raises_clear_error(tmp_path):
|
def test_connection_failure_raises_clear_error(tmp_path):
|
||||||
"""Test that connection failure raises clear error."""
|
"""Test that connection failure raises clear error."""
|
||||||
from mcp_forge.podman.client import PodmanClient, PodmanConnectionError
|
from pod_executor.containers.client import PodmanClient, PodmanConnectionError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -66,7 +66,7 @@ def test_connection_failure_raises_clear_error(tmp_path):
|
||||||
|
|
||||||
def test_socket_path_validation(tmp_path):
|
def test_socket_path_validation(tmp_path):
|
||||||
"""Test that socket path is validated before connecting."""
|
"""Test that socket path is validated before connecting."""
|
||||||
from mcp_forge.podman.client import PodmanClient, PodmanConnectionError
|
from pod_executor.containers.client import PodmanClient, PodmanConnectionError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -89,7 +89,7 @@ def test_socket_path_validation(tmp_path):
|
||||||
|
|
||||||
def test_socket_permissions_check(tmp_path):
|
def test_socket_permissions_check(tmp_path):
|
||||||
"""Test that socket permissions are checked."""
|
"""Test that socket permissions are checked."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -120,7 +120,7 @@ def test_socket_permissions_check(tmp_path):
|
||||||
|
|
||||||
def test_api_version_compatibility_check(tmp_path):
|
def test_api_version_compatibility_check(tmp_path):
|
||||||
"""Test that API version is checked."""
|
"""Test that API version is checked."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -154,7 +154,7 @@ def test_api_version_compatibility_check(tmp_path):
|
||||||
|
|
||||||
def test_ping_health_check(tmp_path):
|
def test_ping_health_check(tmp_path):
|
||||||
"""Test that ping/health check works."""
|
"""Test that ping/health check works."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -184,7 +184,7 @@ def test_ping_health_check(tmp_path):
|
||||||
|
|
||||||
def test_lazy_connection(tmp_path):
|
def test_lazy_connection(tmp_path):
|
||||||
"""Test that connection is lazy (only connects when needed)."""
|
"""Test that connection is lazy (only connects when needed)."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -215,7 +215,7 @@ def test_lazy_connection(tmp_path):
|
||||||
|
|
||||||
def test_disconnect_cleanup(tmp_path):
|
def test_disconnect_cleanup(tmp_path):
|
||||||
"""Test that disconnect cleans up properly."""
|
"""Test that disconnect cleans up properly."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -246,7 +246,7 @@ def test_disconnect_cleanup(tmp_path):
|
||||||
|
|
||||||
def test_connection_error_includes_socket_path(tmp_path):
|
def test_connection_error_includes_socket_path(tmp_path):
|
||||||
"""Test that connection errors include the socket path for debugging."""
|
"""Test that connection errors include the socket path for debugging."""
|
||||||
from mcp_forge.podman.client import PodmanClient, PodmanConnectionError
|
from pod_executor.containers.client import PodmanClient, PodmanConnectionError
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -270,7 +270,7 @@ def test_connection_error_includes_socket_path(tmp_path):
|
||||||
|
|
||||||
def test_client_property_auto_connects(tmp_path):
|
def test_client_property_auto_connects(tmp_path):
|
||||||
"""Test that accessing client property auto-connects if not connected."""
|
"""Test that accessing client property auto-connects if not connected."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
@ -304,7 +304,7 @@ def test_client_property_auto_connects(tmp_path):
|
||||||
|
|
||||||
def test_validator_and_audit_logger_stored(tmp_path):
|
def test_validator_and_audit_logger_stored(tmp_path):
|
||||||
"""Test that validator and audit logger are stored for later use."""
|
"""Test that validator and audit logger are stored for later use."""
|
||||||
from mcp_forge.podman.client import PodmanClient
|
from pod_executor.containers.client import PodmanClient
|
||||||
from mcp_forge.security.audit import AuditLogger
|
from mcp_forge.security.audit import AuditLogger
|
||||||
from mcp_forge.security.allowlist import OperationValidator
|
from mcp_forge.security.allowlist import OperationValidator
|
||||||
from mcp_forge.config.schema import SecurityConfig
|
from mcp_forge.config.schema import SecurityConfig
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import pytest
|
||||||
|
|
||||||
def test_parse_memory_string_megabytes():
|
def test_parse_memory_string_megabytes():
|
||||||
"""Test parsing memory string with megabytes suffix."""
|
"""Test parsing memory string with megabytes suffix."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
result = parse_memory_string("512m")
|
result = parse_memory_string("512m")
|
||||||
assert result == 536870912 # 512 * 1024 * 1024
|
assert result == 536870912 # 512 * 1024 * 1024
|
||||||
|
|
@ -18,7 +18,7 @@ def test_parse_memory_string_megabytes():
|
||||||
|
|
||||||
def test_parse_memory_string_gigabytes():
|
def test_parse_memory_string_gigabytes():
|
||||||
"""Test parsing memory string with gigabytes suffix."""
|
"""Test parsing memory string with gigabytes suffix."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
result = parse_memory_string("2g")
|
result = parse_memory_string("2g")
|
||||||
assert result == 2147483648 # 2 * 1024 * 1024 * 1024
|
assert result == 2147483648 # 2 * 1024 * 1024 * 1024
|
||||||
|
|
@ -26,7 +26,7 @@ def test_parse_memory_string_gigabytes():
|
||||||
|
|
||||||
def test_parse_memory_string_kilobytes():
|
def test_parse_memory_string_kilobytes():
|
||||||
"""Test parsing memory string with kilobytes suffix."""
|
"""Test parsing memory string with kilobytes suffix."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
result = parse_memory_string("1024k")
|
result = parse_memory_string("1024k")
|
||||||
assert result == 1048576 # 1024 * 1024
|
assert result == 1048576 # 1024 * 1024
|
||||||
|
|
@ -34,7 +34,7 @@ def test_parse_memory_string_kilobytes():
|
||||||
|
|
||||||
def test_parse_memory_string_case_insensitive():
|
def test_parse_memory_string_case_insensitive():
|
||||||
"""Test that memory string parsing is case-insensitive."""
|
"""Test that memory string parsing is case-insensitive."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
assert parse_memory_string("512M") == 536870912
|
assert parse_memory_string("512M") == 536870912
|
||||||
assert parse_memory_string("2G") == 2147483648
|
assert parse_memory_string("2G") == 2147483648
|
||||||
|
|
@ -43,7 +43,7 @@ def test_parse_memory_string_case_insensitive():
|
||||||
|
|
||||||
def test_parse_memory_string_invalid_format_raises_value_error():
|
def test_parse_memory_string_invalid_format_raises_value_error():
|
||||||
"""Test that invalid format raises ValueError."""
|
"""Test that invalid format raises ValueError."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
parse_memory_string("invalid")
|
parse_memory_string("invalid")
|
||||||
|
|
@ -58,7 +58,7 @@ def test_parse_memory_string_invalid_format_raises_value_error():
|
||||||
|
|
||||||
def test_parse_memory_string_negative_value_raises_value_error():
|
def test_parse_memory_string_negative_value_raises_value_error():
|
||||||
"""Test that negative values raise ValueError."""
|
"""Test that negative values raise ValueError."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
parse_memory_string("-512m")
|
parse_memory_string("-512m")
|
||||||
|
|
@ -67,7 +67,7 @@ def test_parse_memory_string_negative_value_raises_value_error():
|
||||||
|
|
||||||
def test_parse_memory_string_zero_value_raises_value_error():
|
def test_parse_memory_string_zero_value_raises_value_error():
|
||||||
"""Test that zero value raises ValueError."""
|
"""Test that zero value raises ValueError."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
parse_memory_string("0m")
|
parse_memory_string("0m")
|
||||||
|
|
@ -76,7 +76,7 @@ def test_parse_memory_string_zero_value_raises_value_error():
|
||||||
|
|
||||||
def test_parse_cpu_quota_valid_value():
|
def test_parse_cpu_quota_valid_value():
|
||||||
"""Test that valid CPU quota values are accepted."""
|
"""Test that valid CPU quota values are accepted."""
|
||||||
from mcp_forge.security.resource_limits import parse_cpu_quota
|
from pod_executor.security.resource_limits import parse_cpu_quota
|
||||||
|
|
||||||
result = parse_cpu_quota(50000)
|
result = parse_cpu_quota(50000)
|
||||||
assert result == 50000
|
assert result == 50000
|
||||||
|
|
@ -87,7 +87,7 @@ def test_parse_cpu_quota_valid_value():
|
||||||
|
|
||||||
def test_parse_cpu_quota_max_limit():
|
def test_parse_cpu_quota_max_limit():
|
||||||
"""Test that CPU quota has a reasonable maximum (10 cores)."""
|
"""Test that CPU quota has a reasonable maximum (10 cores)."""
|
||||||
from mcp_forge.security.resource_limits import parse_cpu_quota
|
from pod_executor.security.resource_limits import parse_cpu_quota
|
||||||
|
|
||||||
# Should accept up to 1000000 (10 cores)
|
# Should accept up to 1000000 (10 cores)
|
||||||
result = parse_cpu_quota(1000000)
|
result = parse_cpu_quota(1000000)
|
||||||
|
|
@ -101,7 +101,7 @@ def test_parse_cpu_quota_max_limit():
|
||||||
|
|
||||||
def test_parse_cpu_quota_negative_raises_value_error():
|
def test_parse_cpu_quota_negative_raises_value_error():
|
||||||
"""Test that negative CPU quota raises ValueError."""
|
"""Test that negative CPU quota raises ValueError."""
|
||||||
from mcp_forge.security.resource_limits import parse_cpu_quota
|
from pod_executor.security.resource_limits import parse_cpu_quota
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
parse_cpu_quota(-1)
|
parse_cpu_quota(-1)
|
||||||
|
|
@ -110,7 +110,7 @@ def test_parse_cpu_quota_negative_raises_value_error():
|
||||||
|
|
||||||
def test_parse_cpu_quota_zero_raises_value_error():
|
def test_parse_cpu_quota_zero_raises_value_error():
|
||||||
"""Test that zero CPU quota raises ValueError."""
|
"""Test that zero CPU quota raises ValueError."""
|
||||||
from mcp_forge.security.resource_limits import parse_cpu_quota
|
from pod_executor.security.resource_limits import parse_cpu_quota
|
||||||
|
|
||||||
with pytest.raises(ValueError) as exc_info:
|
with pytest.raises(ValueError) as exc_info:
|
||||||
parse_cpu_quota(0)
|
parse_cpu_quota(0)
|
||||||
|
|
@ -119,7 +119,7 @@ def test_parse_cpu_quota_zero_raises_value_error():
|
||||||
|
|
||||||
def test_parse_storage_string_same_as_memory():
|
def test_parse_storage_string_same_as_memory():
|
||||||
"""Test that storage parsing works the same as memory parsing."""
|
"""Test that storage parsing works the same as memory parsing."""
|
||||||
from mcp_forge.security.resource_limits import parse_storage_string
|
from pod_executor.security.resource_limits import parse_storage_string
|
||||||
|
|
||||||
assert parse_storage_string("1g") == 1073741824
|
assert parse_storage_string("1g") == 1073741824
|
||||||
assert parse_storage_string("512m") == 536870912
|
assert parse_storage_string("512m") == 536870912
|
||||||
|
|
@ -128,7 +128,7 @@ def test_parse_storage_string_same_as_memory():
|
||||||
|
|
||||||
def test_resource_limits_class_initialization():
|
def test_resource_limits_class_initialization():
|
||||||
"""Test ResourceLimits class initializes correctly."""
|
"""Test ResourceLimits class initializes correctly."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
limits = ResourceLimits(
|
limits = ResourceLimits(
|
||||||
memory="512m",
|
memory="512m",
|
||||||
|
|
@ -145,7 +145,7 @@ def test_resource_limits_class_initialization():
|
||||||
|
|
||||||
def test_resource_limits_validates_memory():
|
def test_resource_limits_validates_memory():
|
||||||
"""Test that ResourceLimits validates memory string."""
|
"""Test that ResourceLimits validates memory string."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ResourceLimits(
|
ResourceLimits(
|
||||||
|
|
@ -158,7 +158,7 @@ def test_resource_limits_validates_memory():
|
||||||
|
|
||||||
def test_resource_limits_validates_storage():
|
def test_resource_limits_validates_storage():
|
||||||
"""Test that ResourceLimits validates storage string."""
|
"""Test that ResourceLimits validates storage string."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ResourceLimits(
|
ResourceLimits(
|
||||||
|
|
@ -171,7 +171,7 @@ def test_resource_limits_validates_storage():
|
||||||
|
|
||||||
def test_resource_limits_validates_cpu_quota():
|
def test_resource_limits_validates_cpu_quota():
|
||||||
"""Test that ResourceLimits validates CPU quota."""
|
"""Test that ResourceLimits validates CPU quota."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ResourceLimits(
|
ResourceLimits(
|
||||||
|
|
@ -184,7 +184,7 @@ def test_resource_limits_validates_cpu_quota():
|
||||||
|
|
||||||
def test_resource_limits_to_podman_params():
|
def test_resource_limits_to_podman_params():
|
||||||
"""Test conversion to Podman container parameters."""
|
"""Test conversion to Podman container parameters."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
limits = ResourceLimits(
|
limits = ResourceLimits(
|
||||||
memory="512m",
|
memory="512m",
|
||||||
|
|
@ -205,7 +205,7 @@ def test_resource_limits_to_podman_params():
|
||||||
|
|
||||||
def test_resource_limits_default_timeout():
|
def test_resource_limits_default_timeout():
|
||||||
"""Test that ResourceLimits has a default timeout."""
|
"""Test that ResourceLimits has a default timeout."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
limits = ResourceLimits(
|
limits = ResourceLimits(
|
||||||
memory="512m",
|
memory="512m",
|
||||||
|
|
@ -218,7 +218,7 @@ def test_resource_limits_default_timeout():
|
||||||
|
|
||||||
def test_parse_memory_string_with_spaces():
|
def test_parse_memory_string_with_spaces():
|
||||||
"""Test parsing memory strings that have spaces."""
|
"""Test parsing memory strings that have spaces."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
# Should handle spaces gracefully (strip them)
|
# Should handle spaces gracefully (strip them)
|
||||||
result = parse_memory_string(" 512m ")
|
result = parse_memory_string(" 512m ")
|
||||||
|
|
@ -227,7 +227,7 @@ def test_parse_memory_string_with_spaces():
|
||||||
|
|
||||||
def test_parse_memory_string_bytes_suffix():
|
def test_parse_memory_string_bytes_suffix():
|
||||||
"""Test parsing memory string with bytes suffix (no multiplier)."""
|
"""Test parsing memory string with bytes suffix (no multiplier)."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
# Just a number (bytes) - should this be supported?
|
# Just a number (bytes) - should this be supported?
|
||||||
# Based on architecture, we support k, m, g suffixes
|
# Based on architecture, we support k, m, g suffixes
|
||||||
|
|
@ -238,7 +238,7 @@ def test_parse_memory_string_bytes_suffix():
|
||||||
|
|
||||||
def test_resource_limits_storage_quota_in_podman_params():
|
def test_resource_limits_storage_quota_in_podman_params():
|
||||||
"""Test that storage limits are included in Podman params."""
|
"""Test that storage limits are included in Podman params."""
|
||||||
from mcp_forge.security.resource_limits import ResourceLimits
|
from pod_executor.security.resource_limits import ResourceLimits
|
||||||
|
|
||||||
limits = ResourceLimits(
|
limits = ResourceLimits(
|
||||||
memory="512m",
|
memory="512m",
|
||||||
|
|
@ -256,7 +256,7 @@ def test_resource_limits_storage_quota_in_podman_params():
|
||||||
|
|
||||||
def test_cpu_quota_explanation():
|
def test_cpu_quota_explanation():
|
||||||
"""Test that CPU quota values have clear meaning."""
|
"""Test that CPU quota values have clear meaning."""
|
||||||
from mcp_forge.security.resource_limits import parse_cpu_quota
|
from pod_executor.security.resource_limits import parse_cpu_quota
|
||||||
|
|
||||||
# 100000 = 100% of one CPU core
|
# 100000 = 100% of one CPU core
|
||||||
# 50000 = 50% of one CPU core
|
# 50000 = 50% of one CPU core
|
||||||
|
|
@ -269,7 +269,7 @@ def test_cpu_quota_explanation():
|
||||||
|
|
||||||
def test_parse_memory_with_decimal():
|
def test_parse_memory_with_decimal():
|
||||||
"""Test parsing memory strings with decimal values."""
|
"""Test parsing memory strings with decimal values."""
|
||||||
from mcp_forge.security.resource_limits import parse_memory_string
|
from pod_executor.security.resource_limits import parse_memory_string
|
||||||
|
|
||||||
# Should handle decimals
|
# Should handle decimals
|
||||||
result = parse_memory_string("1.5g")
|
result = parse_memory_string("1.5g")
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from mcp.types import Tool, TextContent
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from mcp_forge.server.tools.execute_python import ExecutePythonTool
|
from mcp_forge.server.tools.execute_python import ExecutePythonTool
|
||||||
from mcp_forge.execution.simple.backend import ExecutionResult
|
from pod_executor.simple.executor import ExecutionResult
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue