fix: Improve and fix codejail service integration
Address recommendations and fixes discussed in PR. Use similar structure to notes app. Fix errors detected in tests.
This commit is contained in:
24
common/lib/capa/capa/safe_exec/exceptions.py
Normal file
24
common/lib/capa/capa/safe_exec/exceptions.py
Normal file
@@ -0,0 +1,24 @@
|
||||
"""
|
||||
Exceptions related to safe exec.
|
||||
"""
|
||||
|
||||
|
||||
class CodejailServiceParseError(Exception):
|
||||
"""
|
||||
An exception that is raised whenever we have issues with data parsing.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
class CodejailServiceStatusError(Exception):
|
||||
"""
|
||||
An exception that is raised whenever Codejail service response status is different to 200.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
|
||||
class CodejailServiceUnavailable(Exception):
|
||||
"""
|
||||
An exception that is raised whenever Codejail service is unavailable.
|
||||
"""
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
97
common/lib/capa/capa/safe_exec/helpers.py
Normal file
97
common/lib/capa/capa/safe_exec/helpers.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""
|
||||
Helper methods related to safe exec.
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
|
||||
from codejail.safe_exec import SafeExecException
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from edx_toggles.toggles import SettingDictToggle
|
||||
from requests.exceptions import RequestException, HTTPError
|
||||
from simplejson import JSONDecodeError
|
||||
|
||||
from .exceptions import CodejailServiceParseError, CodejailServiceStatusError, CodejailServiceUnavailable
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE
|
||||
# .. toggle_implementation: DjangoSetting
|
||||
# .. 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
|
||||
# .. toggle_target_removal_date: None
|
||||
# .. toggle_warnings:
|
||||
# .. toggle_tickets:
|
||||
ENABLE_CODEJAIL_REST_SERVICE = SettingDictToggle(
|
||||
"FEATURES", "ENABLE_CODEJAIL_REST_SERVICE", default=False, module_name=__name__
|
||||
)
|
||||
|
||||
|
||||
def is_codejail_rest_service_enabled():
|
||||
return ENABLE_CODEJAIL_REST_SERVICE.is_enabled()
|
||||
|
||||
|
||||
def get_codejail_rest_service_endpoint():
|
||||
return "".join([
|
||||
settings.CODE_JAIL_REST_SERVICE_HOST,
|
||||
"/api/v0/code-exec"])
|
||||
|
||||
|
||||
def send_safe_exec_request(data, extra_files):
|
||||
"""
|
||||
Sends a request to a codejail api service forwarding required code and files.
|
||||
Arguments:
|
||||
data: Dict containing code and othe parameters
|
||||
required for jailed code execution.
|
||||
extra_files: python_lib.zip file containing extra files
|
||||
required by the codejail execution.
|
||||
Returns:
|
||||
Response received from codejsail api service
|
||||
"""
|
||||
globals_dict = data["globals_dict"]
|
||||
|
||||
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:
|
||||
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.")) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except HTTPError:
|
||||
raise CodejailServiceStatusError(_("Codejail API Service invalid response.")) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
try:
|
||||
response_json = response.json()
|
||||
except JSONDecodeError:
|
||||
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.")) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
emsg = response_json.get("emsg")
|
||||
exception = None
|
||||
|
||||
if emsg:
|
||||
exception_msg = ". ".join([
|
||||
emsg,
|
||||
"For more information check Codejail Service logs."])
|
||||
|
||||
exception = SafeExecException(exception_msg)
|
||||
|
||||
globals_dict.update(response_json.get("globals_dict"))
|
||||
|
||||
return emsg, exception
|
||||
@@ -2,18 +2,16 @@
|
||||
|
||||
|
||||
import hashlib
|
||||
import requests
|
||||
import json
|
||||
|
||||
from codejail.safe_exec import SafeExecException, json_safe
|
||||
from codejail.safe_exec import not_safe_exec as codejail_not_safe_exec
|
||||
from codejail.safe_exec import safe_exec as codejail_safe_exec
|
||||
from django.conf import settings
|
||||
from edx_django_utils.monitoring import function_trace
|
||||
import six
|
||||
from six import text_type
|
||||
|
||||
from . import lazymod
|
||||
from .helpers import is_codejail_rest_service_enabled, send_safe_exec_request
|
||||
|
||||
# Establish the Python environment for Capa.
|
||||
# Capa assumes float-friendly division always.
|
||||
@@ -146,40 +144,18 @@ def safe_exec(
|
||||
# Create the complete code we'll run.
|
||||
code_prolog = CODE_PROLOG % random_seed
|
||||
|
||||
if settings.FEATURES.get('ENABLE_CODEJAIL_REST_SERVICE', False):
|
||||
try:
|
||||
codejail_service_endpoint = "".join([
|
||||
settings.CODE_JAIL_REST_SERVICE_HOST,
|
||||
"/api/v0/code-exec"
|
||||
])
|
||||
data = {
|
||||
"code": code_prolog + LAZY_IMPORTS + code,
|
||||
"globals_dict": globals_dict,
|
||||
"python_path": python_path,
|
||||
"limit_overrides_context": limit_overrides_context,
|
||||
"slug": slug,
|
||||
"unsafely": unsafely
|
||||
}
|
||||
datajson = json.dumps(data)
|
||||
response = requests.request(
|
||||
"POST",
|
||||
codejail_service_endpoint,
|
||||
files=extra_files,
|
||||
data={'payload': datajson}
|
||||
)
|
||||
response_json = response.json()
|
||||
emsg = response_json["emsg"]
|
||||
if emsg:
|
||||
exception_msg = ". ".join([
|
||||
emsg,
|
||||
"For more information check Codejail Service logs."
|
||||
])
|
||||
|
||||
exception = SafeExecException(emsg)
|
||||
globals_dict.update(response_json["globals_dict"])
|
||||
except Exception as e:
|
||||
raise e
|
||||
emsg = None
|
||||
if is_codejail_rest_service_enabled():
|
||||
data = {
|
||||
"code": code_prolog + LAZY_IMPORTS + code,
|
||||
"globals_dict": globals_dict,
|
||||
"python_path": python_path,
|
||||
"limit_overrides_context": limit_overrides_context,
|
||||
"slug": slug,
|
||||
"unsafely": unsafely
|
||||
}
|
||||
|
||||
emsg, exception = send_safe_exec_request(data, extra_files)
|
||||
|
||||
else:
|
||||
# Decide which code executor to use.
|
||||
if unsafely:
|
||||
|
||||
@@ -948,17 +948,17 @@ FEATURES = {
|
||||
# .. toggle_target_removal_date: 2021-10-01
|
||||
# .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405'
|
||||
'ENABLE_V2_CERT_DISPLAY_SETTINGS': False,
|
||||
|
||||
|
||||
# .. toggle_name: ENABLE_CODEJAIL_REST_SERVICE
|
||||
# .. toggle_implementation: DjangoSetting
|
||||
# .. 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: tutor
|
||||
# .. toggle_use_cases: open_edx
|
||||
# .. toggle_creation_date: 2021-08-19
|
||||
# .. toggle_target_removal_date: None
|
||||
# .. toggle_warnings:
|
||||
# .. toggle_tickets:
|
||||
# .. toggle_warnings:
|
||||
# .. toggle_tickets:
|
||||
'ENABLE_CODEJAIL_REST_SERVICE': False,
|
||||
}
|
||||
|
||||
@@ -1643,6 +1643,9 @@ COURSES_WITH_UNSAFE_CODE = []
|
||||
|
||||
# Cojail REST service
|
||||
CODE_JAIL_REST_SERVICE_HOST = 'http://127.0.0.1:8550'
|
||||
CODE_JAIL_REST_SERVICE_CONNECT_TIMEOUT = 0.5 # time in seconds
|
||||
CODE_JAIL_REST_SERVICE_READ_TIMEOUT = 3.5 # time in seconds
|
||||
|
||||
|
||||
############################### DJANGO BUILT-INS ###############################
|
||||
# Change DEBUG in your environment settings files, not here
|
||||
|
||||
Reference in New Issue
Block a user