mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
Allow custom user (#11013)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
parent
5452abe513
commit
37daf068c5
@ -21,6 +21,7 @@ from openhands.events.event_store_abc import EventStoreABC
|
||||
from openhands.events.observation import AgentStateChangedObservation
|
||||
from openhands.events.stream import EventStreamSubscriber
|
||||
from openhands.llm.llm_registry import LLMRegistry
|
||||
from openhands.runtime.runtime_status import RuntimeStatus
|
||||
from openhands.server.config.server_config import ServerConfig
|
||||
from openhands.server.conversation_manager.conversation_manager import (
|
||||
ConversationManager,
|
||||
@ -686,6 +687,7 @@ class ClusteredConversationManager(StandaloneConversationManager):
|
||||
url=self._get_conversation_url(conversation_id),
|
||||
session_api_key=None,
|
||||
event_store=EventStore(conversation_id, self.file_store, uid),
|
||||
runtime_status=RuntimeStatus.READY,
|
||||
)
|
||||
)
|
||||
return results
|
||||
|
||||
@ -60,9 +60,14 @@ from openhands.utils.utils import create_registry_and_conversation_stats
|
||||
RUNTIME_URL_PATTERN = os.getenv(
|
||||
'RUNTIME_URL_PATTERN', 'https://{runtime_id}.prod-runtime.all-hands.dev'
|
||||
)
|
||||
RUNTIME_ROUTING_MODE = os.getenv('RUNTIME_ROUTING_MODE', 'subdomain').lower()
|
||||
|
||||
# Pattern for base URL for the runtime
|
||||
RUNTIME_CONVERSATION_URL = RUNTIME_URL_PATTERN + '/api/conversations/{conversation_id}'
|
||||
RUNTIME_CONVERSATION_URL = RUNTIME_URL_PATTERN + (
|
||||
'/runtime/api/conversations/{conversation_id}'
|
||||
if RUNTIME_ROUTING_MODE == 'path'
|
||||
else '/api/conversations/{conversation_id}'
|
||||
)
|
||||
|
||||
# Time in seconds before a Redis entry is considered expired if not refreshed
|
||||
_REDIS_ENTRY_TIMEOUT_SECONDS = 300
|
||||
|
||||
@ -129,11 +129,15 @@ class ActionExecutionClient(Runtime):
|
||||
return send_request(self.session, method, url, **kwargs)
|
||||
|
||||
def check_if_alive(self) -> None:
|
||||
request_url = f'{self.action_execution_server_url}/alive'
|
||||
self.log('debug', f'Sending request to: {request_url}')
|
||||
response = self._send_action_server_request(
|
||||
'GET',
|
||||
f'{self.action_execution_server_url}/alive',
|
||||
request_url,
|
||||
timeout=5,
|
||||
)
|
||||
self.log('debug', f'Response status code: {response.status_code}')
|
||||
self.log('debug', f'Response text: {response.text}')
|
||||
assert response.is_closed
|
||||
|
||||
def list_files(self, path: str | None = None) -> list[str]:
|
||||
|
||||
@ -415,11 +415,19 @@ class RemoteRuntime(ActionExecutionClient):
|
||||
|
||||
def _wait_until_alive_impl(self) -> None:
|
||||
self.log('debug', f'Waiting for runtime to be alive at url: {self.runtime_url}')
|
||||
self.log(
|
||||
'debug',
|
||||
f'Sending request to: {self.config.sandbox.remote_runtime_api_url}/runtime/{self.runtime_id}',
|
||||
)
|
||||
runtime_info_response = self._send_runtime_api_request(
|
||||
'GET',
|
||||
f'{self.config.sandbox.remote_runtime_api_url}/runtime/{self.runtime_id}',
|
||||
)
|
||||
runtime_data = runtime_info_response.json()
|
||||
self.log(
|
||||
'debug',
|
||||
f'received response: {runtime_data}',
|
||||
)
|
||||
assert 'runtime_id' in runtime_data
|
||||
assert runtime_data['runtime_id'] == self.runtime_id
|
||||
assert 'pod_status' in runtime_data
|
||||
|
||||
@ -13,6 +13,15 @@ from openhands.runtime.plugins.requirement import Plugin, PluginRequirement
|
||||
from openhands.runtime.utils import find_available_tcp_port
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
SU_TO_USER = os.getenv('SU_TO_USER', 'true').lower() in (
|
||||
'1',
|
||||
'true',
|
||||
't',
|
||||
'yes',
|
||||
'y',
|
||||
'on',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class JupyterRequirement(PluginRequirement):
|
||||
@ -36,7 +45,7 @@ class JupyterPlugin(Plugin):
|
||||
|
||||
if not is_local_runtime:
|
||||
# Non-LocalRuntime
|
||||
prefix = f'su - {username} -s '
|
||||
prefix = f'su - {username} -s ' if SU_TO_USER else ''
|
||||
# cd to code repo, setup all env vars and run micromamba
|
||||
poetry_prefix = (
|
||||
'cd /openhands/code\n'
|
||||
|
||||
@ -15,6 +15,16 @@ from openhands.runtime.plugins.requirement import Plugin, PluginRequirement
|
||||
from openhands.runtime.utils.system import check_port_available
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME')
|
||||
SU_TO_USER = os.getenv('SU_TO_USER', 'true').lower() in (
|
||||
'1',
|
||||
'true',
|
||||
't',
|
||||
'yes',
|
||||
'y',
|
||||
'on',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class VSCodeRequirement(PluginRequirement):
|
||||
@ -37,7 +47,7 @@ class VSCodePlugin(Plugin):
|
||||
)
|
||||
return
|
||||
|
||||
if username not in ['root', 'openhands']:
|
||||
if username not in filter(None, [RUNTIME_USERNAME, 'root', 'openhands']):
|
||||
self.vscode_port = None
|
||||
self.vscode_connection_token = None
|
||||
logger.warning(
|
||||
@ -83,13 +93,19 @@ class VSCodePlugin(Plugin):
|
||||
if path_mode:
|
||||
base_path_flag = f' --server-base-path /{runtime_id}/vscode'
|
||||
|
||||
cmd = (
|
||||
f"su - {username} -s /bin/bash << 'EOF'\n"
|
||||
f'sudo chown -R {username}:{username} /openhands/.openvscode-server\n'
|
||||
f'cd {workspace_path}\n'
|
||||
f'exec /openhands/.openvscode-server/bin/openvscode-server --host 0.0.0.0 --connection-token {self.vscode_connection_token} --port {self.vscode_port} --disable-workspace-trust{base_path_flag}\n'
|
||||
'EOF'
|
||||
)
|
||||
cmd = (
|
||||
(
|
||||
f"su - {username} -s /bin/bash << 'EOF'\n"
|
||||
if SU_TO_USER
|
||||
else "/bin/bash << 'EOF'\n"
|
||||
)
|
||||
+ f'sudo chown -R {username}:{username} /openhands/.openvscode-server\n'
|
||||
+ f'cd {workspace_path}\n'
|
||||
+ 'exec /openhands/.openvscode-server/bin/openvscode-server '
|
||||
+ f'--host 0.0.0.0 --connection-token {self.vscode_connection_token} '
|
||||
+ f'--port {self.vscode_port} --disable-workspace-trust{base_path_flag}\n'
|
||||
+ 'EOF'
|
||||
)
|
||||
|
||||
# Using asyncio.create_subprocess_shell instead of subprocess.Popen
|
||||
# to avoid ASYNC101 linting error
|
||||
|
||||
@ -20,6 +20,16 @@ from openhands.events.observation.commands import (
|
||||
from openhands.runtime.utils.bash_constants import TIMEOUT_MESSAGE_TEMPLATE
|
||||
from openhands.utils.shutdown_listener import should_continue
|
||||
|
||||
RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME')
|
||||
SU_TO_USER = os.getenv('SU_TO_USER', 'true').lower() in (
|
||||
'1',
|
||||
'true',
|
||||
't',
|
||||
'yes',
|
||||
'y',
|
||||
'on',
|
||||
)
|
||||
|
||||
|
||||
def split_bash_commands(commands: str) -> list[str]:
|
||||
if not commands.strip():
|
||||
@ -193,7 +203,9 @@ class BashSession:
|
||||
def initialize(self) -> None:
|
||||
self.server = libtmux.Server()
|
||||
_shell_command = '/bin/bash'
|
||||
if self.username in ['root', 'openhands']:
|
||||
if SU_TO_USER and self.username in list(
|
||||
filter(None, [RUNTIME_USERNAME, 'root', 'openhands'])
|
||||
):
|
||||
# This starts a non-login (new) shell for the given user
|
||||
_shell_command = f'su {self.username} -'
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
from openhands.core.config import OpenHandsConfig
|
||||
from openhands.core.logger import openhands_logger as logger
|
||||
from openhands.runtime.plugins import PluginRequirement
|
||||
@ -12,6 +14,9 @@ DEFAULT_PYTHON_PREFIX = [
|
||||
]
|
||||
DEFAULT_MAIN_MODULE = 'openhands.runtime.action_execution_server'
|
||||
|
||||
RUNTIME_USERNAME = os.getenv('RUNTIME_USERNAME')
|
||||
RUNTIME_UID = os.getenv('RUNTIME_UID')
|
||||
|
||||
|
||||
def get_action_execution_server_startup_command(
|
||||
server_port: int,
|
||||
@ -26,7 +31,10 @@ def get_action_execution_server_startup_command(
|
||||
sandbox_config = app_config.sandbox
|
||||
logger.debug(f'app_config {vars(app_config)}')
|
||||
logger.debug(f'sandbox_config {vars(sandbox_config)}')
|
||||
logger.debug(f'override_user_id {override_user_id}')
|
||||
logger.debug(f'RUNTIME_USERNAME {RUNTIME_USERNAME}, RUNTIME_UID {RUNTIME_UID}')
|
||||
logger.debug(
|
||||
f'override_username {override_username}, override_user_id {override_user_id}'
|
||||
)
|
||||
|
||||
# Plugin args
|
||||
plugin_args = []
|
||||
@ -40,10 +48,15 @@ def get_action_execution_server_startup_command(
|
||||
'--browsergym-eval-env'
|
||||
] + sandbox_config.browsergym_eval_env.split(' ')
|
||||
|
||||
username = override_username or (
|
||||
'openhands' if app_config.run_as_openhands else 'root'
|
||||
username = (
|
||||
override_username
|
||||
or RUNTIME_USERNAME
|
||||
or ('openhands' if app_config.run_as_openhands else 'root')
|
||||
)
|
||||
user_id = override_user_id or (1000 if app_config.run_as_openhands else 0)
|
||||
user_id = (
|
||||
override_user_id or RUNTIME_UID or (1000 if app_config.run_as_openhands else 0)
|
||||
)
|
||||
logger.debug(f'username {username}, user_id {user_id}')
|
||||
|
||||
base_cmd = [
|
||||
*python_prefix,
|
||||
|
||||
@ -27,6 +27,7 @@ class HttpSession:
|
||||
headers = kwargs.get('headers') or {}
|
||||
headers = {**self.headers, **headers}
|
||||
kwargs['headers'] = headers
|
||||
logger.debug(f'HttpSession:request called with args {args} and kwargs {kwargs}')
|
||||
return CLIENT.request(*args, **kwargs)
|
||||
|
||||
def stream(self, *args, **kwargs):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user