As part of dissolving our sub-projects in edx-platform, we are moving this package under the xmodule directory. We have fixed all the occurences of import of this package and also fixed all documents related references. This might break your platform if you have any reference of `import capa` or `from capa import` in your codebase or in any Xblock. Ref: https://openedx.atlassian.net/browse/BOM-2582
108 lines
3.8 KiB
Python
108 lines
3.8 KiB
Python
"""
|
|
Helper methods related to safe exec.
|
|
"""
|
|
|
|
import json
|
|
import logging
|
|
from importlib import import_module
|
|
import requests
|
|
|
|
from codejail.safe_exec import SafeExecException
|
|
from django.conf import settings
|
|
from edx_toggles.toggles import SettingToggle
|
|
from requests.exceptions import RequestException, HTTPError
|
|
from simplejson import JSONDecodeError
|
|
|
|
from django.utils.translation import gettext as _
|
|
from .exceptions import CodejailServiceParseError, CodejailServiceStatusError, CodejailServiceUnavailable
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
# .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE
|
|
# .. toggle_implementation: SettingToggle
|
|
# .. toggle_default: False
|
|
# .. toggle_description: Set this to True if you want to run Codejail code using
|
|
# a separate VM or container and communicate with edx-platform using REST API.
|
|
# .. toggle_use_cases: open_edx
|
|
# .. toggle_creation_date: 2021-08-19
|
|
ENABLE_CODEJAIL_REST_SERVICE = SettingToggle(
|
|
"ENABLE_CODEJAIL_REST_SERVICE", default=False, module_name=__name__
|
|
)
|
|
|
|
|
|
def is_codejail_rest_service_enabled():
|
|
return ENABLE_CODEJAIL_REST_SERVICE.is_enabled()
|
|
|
|
|
|
def get_remote_exec(*args, **kwargs):
|
|
"""Get remote exec function based on setting and executes it."""
|
|
remote_exec_function_name = settings.CODE_JAIL_REST_SERVICE_REMOTE_EXEC
|
|
try:
|
|
mod_name, func_name = remote_exec_function_name.rsplit('.', 1)
|
|
remote_exec_module = import_module(mod_name)
|
|
remote_exec_function = getattr(remote_exec_module, func_name)
|
|
if not remote_exec_function:
|
|
remote_exec_function = send_safe_exec_request_v0
|
|
except ModuleNotFoundError:
|
|
return send_safe_exec_request_v0(*args, **kwargs)
|
|
return remote_exec_function(*args, **kwargs)
|
|
|
|
|
|
def get_codejail_rest_service_endpoint():
|
|
return f"{settings.CODE_JAIL_REST_SERVICE_HOST}/api/v0/code-exec"
|
|
|
|
|
|
def send_safe_exec_request_v0(data):
|
|
"""
|
|
Sends a request to a codejail api service forwarding required code and files.
|
|
Arguments:
|
|
data: Dict containing code and other parameters
|
|
required for jailed code execution.
|
|
It also includes extra_files (python_lib.zip) required by the codejail execution.
|
|
Returns:
|
|
Response received from codejail api service
|
|
"""
|
|
globals_dict = data["globals_dict"]
|
|
extra_files = data.pop("extra_files")
|
|
|
|
codejail_service_endpoint = get_codejail_rest_service_endpoint()
|
|
payload = json.dumps(data)
|
|
|
|
try:
|
|
response = requests.post(
|
|
codejail_service_endpoint,
|
|
files=extra_files,
|
|
data={'payload': payload},
|
|
timeout=(settings.CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT, settings.CODE_JAIL_REST_SERVICE_READ_TIMEOUT)
|
|
)
|
|
|
|
except RequestException as err:
|
|
log.error("Failed to connect to codejail api service: url=%s, params=%s",
|
|
codejail_service_endpoint, str(payload))
|
|
raise CodejailServiceUnavailable(_(
|
|
"Codejail API Service is unavailable. "
|
|
"Please try again in a few minutes."
|
|
)) from err
|
|
|
|
try:
|
|
response.raise_for_status()
|
|
except HTTPError as err:
|
|
raise CodejailServiceStatusError(_("Codejail API Service invalid response.")) from err
|
|
|
|
try:
|
|
response_json = response.json()
|
|
except JSONDecodeError as err:
|
|
log.error("Invalid JSON response received from codejail api service: Response_Content=%s", response.content)
|
|
raise CodejailServiceParseError(_("Invalid JSON response received from codejail api service.")) from err
|
|
|
|
emsg = response_json.get("emsg")
|
|
exception = None
|
|
|
|
if emsg:
|
|
exception_msg = f"{emsg}. For more information check Codejail Service logs."
|
|
exception = SafeExecException(exception_msg)
|
|
|
|
globals_dict.update(response_json.get("globals_dict"))
|
|
|
|
return emsg, exception
|