From 4ea74219283a2cbbb2c4e738c7fe8a30736901d4 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Tue, 25 Jun 2024 13:55:02 -0400 Subject: [PATCH 01/11] feat: convert paths back to vars Paths referenced in the output of a given job, particularly those as part of a GradingScriptCommandResponse's stdout/stderr/cmd props, should be reverted back to their original variables created in the do_grading file. --- .../common/grading_job/grading_job_result.py | 65 ++++--- .../common/services/push_results.py | 11 +- .../build_script/preprocess/preprocessor.py | 167 +++++++++--------- worker/orca_grader/container/do_grading.py | 10 +- .../grading_script/grading_script_command.py | 13 +- .../grading_script_command_response.py | 108 +++++------ 6 files changed, 202 insertions(+), 172 deletions(-) diff --git a/worker/orca_grader/common/grading_job/grading_job_result.py b/worker/orca_grader/common/grading_job/grading_job_result.py index 40942d4e..b76d8fb1 100644 --- a/worker/orca_grader/common/grading_job/grading_job_result.py +++ b/worker/orca_grader/common/grading_job/grading_job_result.py @@ -1,31 +1,42 @@ -from typing import List, Optional +from typing import Dict, List, Optional from orca_grader.container.grading_script.grading_script_command_response import GradingScriptCommandResponse from orca_grader.common.types.grading_job_json_types import GradingJobOutputJSON, GradingJobOutputJSON + class GradingJobResult: - - def __init__(self, command_responses: List[GradingScriptCommandResponse], - execution_errors: List[Exception] = [], - output: str = None) -> None: - self.__command_responses = command_responses - self.__execution_errors = execution_errors - self.__output = output - - def get_command_responses(self) -> List[GradingScriptCommandResponse]: - return self.__command_responses - - def get_output(self) -> Optional[str]: - return self.__output - - def get_execution_errors(self) -> List[Exception]: - return self.__execution_errors - - def to_json(self) -> GradingJobOutputJSON: - result = dict() - json_responses = list(map(lambda c: c.to_json(), self.__command_responses)) - result["shell_responses"] = json_responses - if self.__execution_errors is not None: - result["errors"] = list(map(lambda e: f"{e.__class__.__name__}: {e}", self.__execution_errors)) - if self.__output is not None: - result["output"] = self.__output - return result + + def __init__(self, command_responses: List[GradingScriptCommandResponse], + execution_errors: List[Exception] = [], + output: str = None) -> None: + self.__command_responses = command_responses + self.__execution_errors = execution_errors + self.__output = output + + def get_command_responses(self) -> List[GradingScriptCommandResponse]: + return self.__command_responses + + def get_output(self) -> Optional[str]: + return self.__output + + def get_execution_errors(self) -> List[Exception]: + return self.__execution_errors + + def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> GradingJobOutputJSON: + result = dict() + json_responses = list( + map( + lambda c: c.to_json(interpolated_dirs=interpolated_dirs), + self.__command_responses + ) + ) + result["shell_responses"] = json_responses + if self.__execution_errors is not None: + result["errors"] = list( + map( + lambda e: f"{e.__class__.__name__}: {e}", + self.__execution_errors + ) + ) + if self.__output is not None: + result["output"] = self.__output + return result diff --git a/worker/orca_grader/common/services/push_results.py b/worker/orca_grader/common/services/push_results.py index af4162c9..4e18c115 100644 --- a/worker/orca_grader/common/services/push_results.py +++ b/worker/orca_grader/common/services/push_results.py @@ -2,6 +2,7 @@ import time import requests from requests import HTTPError +from typing import Dict, Optional from orca_grader.common.grading_job.grading_job_result import GradingJobResult from orca_grader.common.services.exceptions import PushResultsFailureException from orca_grader.common.types.grading_job_json_types import GradingJobJSON @@ -11,16 +12,18 @@ # TODO: Update if POST data format changes, or simply remove this comment once solidifed. -def push_results_to_response_url(job_result: GradingJobResult, key: str, - response_url: str) -> None: - result_as_json = job_result.to_json() +def push_results_to_response_url(job_result: GradingJobResult, + key: str, + response_url: str, + interpolated_dirs: Optional[Dict[str, str]]) -> None: + result_as_json = job_result.to_json(interpolated_dirs=interpolated_dirs) result_as_json["key"] = key _send_results_with_exponential_backoff(result_as_json, response_url) def push_results_with_exception(grading_job: GradingJobJSON, e: Exception) -> None: - output = GradingJobResult([], [e]) + output = GradingJobResult([], [e]).to_json() key, response_url = grading_job["key"], grading_job["response_url"] push_results_to_response_url(output, key, response_url) diff --git a/worker/orca_grader/container/build_script/preprocess/preprocessor.py b/worker/orca_grader/container/build_script/preprocess/preprocessor.py index b14e8665..f160cfb3 100644 --- a/worker/orca_grader/container/build_script/preprocess/preprocessor.py +++ b/worker/orca_grader/container/build_script/preprocess/preprocessor.py @@ -11,91 +11,98 @@ from orca_grader.container.build_script.json_helpers.grading_script_command import RESERVED_KEYWORDS, is_bash_command, is_conditional_command from orca_grader.common.types.grading_job_json_types import GradingScriptCommandJSON -DEFAULT_COMMAND_TIMEOUT = 60 # 1 minute +DEFAULT_COMMAND_TIMEOUT = 60 # 1 minute + class GradingScriptPreprocessor: - def __init__(self, secret: str, json_cmds: List[GradingScriptCommandJSON], - code_files: Dict[str, CodeFileInfo], code_file_processor: CodeFileProcessor, - cmd_timeout: int = DEFAULT_COMMAND_TIMEOUT) -> None: - flattened_cmds = flatten_grading_script(json_cmds) - if CycleDetector.contains_cycle(flattened_cmds): - raise NotADAGException() - self.__interpolated_dirs = { - "$DOWNLOADED": f"{secret}/downloaded", - "$EXTRACTED": f"{secret}/extracted", - "$BUILD": f"{secret}/build" - } - self.__code_file_processor = code_file_processor - self.__json_cmds = flattened_cmds - self.__code_files = code_files - self.__cmds = [None for _ in range(len(flattened_cmds))] - self.__cmd_timeout = cmd_timeout - - def preprocess_job(self) -> GradingScriptCommand: - self.__download_and_process_code_files() - script = self.__generate_grading_script() - return script + def __init__(self, secret: str, json_cmds: List[GradingScriptCommandJSON], + code_files: Dict[str, CodeFileInfo], code_file_processor: CodeFileProcessor, + cmd_timeout: int = DEFAULT_COMMAND_TIMEOUT) -> None: + flattened_cmds = flatten_grading_script(json_cmds) + if CycleDetector.contains_cycle(flattened_cmds): + raise NotADAGException() + self.__interpolated_dirs = { + "$DOWNLOADED": f"{secret}/downloaded", + "$EXTRACTED": f"{secret}/extracted", + "$BUILD": f"{secret}/build" + } + self.__code_file_processor = code_file_processor + self.__json_cmds = flattened_cmds + self.__code_files = code_files + self.__cmds = [None for _ in range(len(flattened_cmds))] + self.__cmd_timeout = cmd_timeout + + def preprocess_job(self) -> GradingScriptCommand: + self.__download_and_process_code_files() + script = self.__generate_grading_script() + return script + + def __download_and_process_code_files(self) -> None: + """ + Given a list of CodeFileInfo objects, download and extract (if necessary) each one. + """ + self.__create_script_dirs() + download_dir = self.__interpolated_dirs["$DOWNLOADED"] + extract_dir = self.__interpolated_dirs["$EXTRACTED"] + for name, code_file in self.__code_files.items(): + file_download_dir = os.path.join(download_dir, name) + file_extract_dir = os.path.join(extract_dir, name) + self.__code_file_processor.process_file(code_file, file_download_dir, + file_extract_dir) + + def __generate_grading_script(self) -> GradingScriptCommand: + for i in range(len(self.__cmds)): + if self.__cmds[i] is None: + self.__get_grading_command_by_index(i) + return self.__cmds[0] - def __download_and_process_code_files(self) -> None: - """ - Given a list of CodeFileInfo objects, download and extract (if necessary) each one. - """ - self.__create_script_dirs() - download_dir = self.__interpolated_dirs["$DOWNLOADED"] - extract_dir = self.__interpolated_dirs["$EXTRACTED"] - for name, code_file in self.__code_files.items(): - file_download_dir = os.path.join(download_dir, name) - file_extract_dir = os.path.join(extract_dir, name) - self.__code_file_processor.process_file(code_file, file_download_dir, - file_extract_dir) + def __get_grading_command_by_index(self, index: int) -> GradingScriptCommand: + if self.__cmds[index] is not None: + return self.__cmds[index] + if is_bash_command(self.__json_cmds[index]): + return self.__process_bash_command_json(self.__json_cmds[index], index) + elif is_conditional_command(self.__json_cmds[index]): + return self.__process_conditional_command_json(self.__json_cmds[index], index) + else: + raise InvalidGradingScriptCommand() - def __generate_grading_script(self) -> GradingScriptCommand: - for i in range(len(self.__cmds)): - if self.__cmds[i] is None: - self.__get_grading_command_by_index(i) - return self.__cmds[0] + def __process_bash_command_json(self, json_command: GradingScriptCommandJSON, index: int) -> GradingScriptCommand: + shell_cmd: str | List[str] = self.__add_interpolated_paths( + json_command["cmd"]) + on_fail, on_complete = json_command["on_fail"], json_command["on_complete"] + working_dir = self.__add_interpolated_paths( + json_command["working_dir"]) if "working_dir" in json_command else None + cmd = BashGradingScriptCommand(shell_cmd, + on_complete=self.__get_grading_command_by_index( + on_complete) if on_complete != "output" else None, + on_fail=self.__get_grading_command_by_index( + on_fail) if on_fail != "abort" else None, + timeout=json_command["timeout"] if "timeout" in json_command else self.__cmd_timeout, + working_dir=working_dir) + self.__cmds[index] = cmd + return cmd - def __get_grading_command_by_index(self, index: int) -> GradingScriptCommand: - if self.__cmds[index] is not None: - return self.__cmds[index] - if is_bash_command(self.__json_cmds[index]): - return self.__process_bash_command_json(self.__json_cmds[index], index) - elif is_conditional_command(self.__json_cmds[index]): - return self.__process_conditional_command_json(self.__json_cmds[index], index) - else: - raise InvalidGradingScriptCommand() + def __process_conditional_command_json(self, json_command: GradingScriptCommandJSON, index: int): + conditional: Dict[str, str] = json_command["condition"] + predicate: GradingScriptPredicate = GradingScriptPredicate( + conditional["predicate"]) + fs_path: str = self.__add_interpolated_paths(conditional["path"]) + on_false, on_true = json_command["on_false"], json_command["on_true"] + cmd = ConditionalGradingScriptCommand(self.__get_grading_command_by_index(on_true), + self.__get_grading_command_by_index(on_false), fs_path, predicate) + self.__cmds[index] = cmd + return cmd - def __process_bash_command_json(self, json_command: GradingScriptCommandJSON, index: int) -> GradingScriptCommand: - shell_cmd: str | List[str] = self.__add_interpolated_paths(json_command["cmd"]) - on_fail, on_complete = json_command["on_fail"], json_command["on_complete"] - working_dir = self.__add_interpolated_paths(json_command["working_dir"]) if "working_dir" in json_command else None - cmd = BashGradingScriptCommand(shell_cmd, - on_complete=self.__get_grading_command_by_index(on_complete) if on_complete != "output" else None, - on_fail=self.__get_grading_command_by_index(on_fail) if on_fail != "abort" else None, - timeout=json_command["timeout"] if "timeout" in json_command else self.__cmd_timeout, - working_dir=working_dir) - self.__cmds[index] = cmd - return cmd - - def __process_conditional_command_json(self, json_command: GradingScriptCommandJSON, index: int): - conditional: Dict[str, str] = json_command["condition"] - predicate: GradingScriptPredicate = GradingScriptPredicate(conditional["predicate"]) - fs_path: str = self.__add_interpolated_paths(conditional["path"]) - on_false, on_true = json_command["on_false"], json_command["on_true"] - cmd = ConditionalGradingScriptCommand(self.__get_grading_command_by_index(on_true), - self.__get_grading_command_by_index(on_false), fs_path, predicate) - self.__cmds[index] = cmd - return cmd + def __add_interpolated_paths(self, cmd: str | List[str]) -> str | List[str]: + formatted_cmd = cmd + for var in self.__interpolated_dirs: + formatted_cmd = formatted_cmd.replace(var, self.__interpolated_dirs[var]) if type(cmd) == str \ + else list(map(lambda prog_arg: prog_arg.replace(var, self.__interpolated_dirs[var]), formatted_cmd)) + return formatted_cmd - def __add_interpolated_paths(self, cmd: str | List[str]) -> str | List[str]: - formatted_cmd = cmd - for var in self.__interpolated_dirs: - formatted_cmd = formatted_cmd.replace(var, self.__interpolated_dirs[var]) if type(cmd) == str \ - else list(map(lambda prog_arg: prog_arg.replace(var, self.__interpolated_dirs[var]), formatted_cmd)) - return formatted_cmd - - def __create_script_dirs(self) -> None: - for item in self.__interpolated_dirs.items(): - path_var, dir = item - os.makedirs(dir, exist_ok=(path_var == "$ASSETS")) # If the download, extract, or build dir already exists, something has gone very wrong... + def __create_script_dirs(self) -> None: + for item in self.__interpolated_dirs.items(): + path_var, dir = item + # If the download, extract, or build dir already exists, something has gone very wrong... + os.makedirs(dir, exist_ok=(path_var == "$ASSETS")) diff --git a/worker/orca_grader/container/do_grading.py b/worker/orca_grader/container/do_grading.py index 5c2e9ffd..91252434 100644 --- a/worker/orca_grader/container/do_grading.py +++ b/worker/orca_grader/container/do_grading.py @@ -44,11 +44,11 @@ def do_grading(secret: str, grading_job_json: GradingJobJSON) -> GradingJobResul output = GradingJobResult(command_responses, [preprocess_e]) except Exception as e: output = GradingJobResult(command_responses, [e]) - print(output.to_json()) + print(output.to_json(interpolated_dirs=interpolated_dirs)) push_results_to_response_url(output, grading_job_json["key"], - grading_job_json["container_response_url"] if - "container_response_url" in grading_job_json else + grading_job_json["container_response_url"] + if "container_response_url" in grading_job_json else grading_job_json["response_url"]) return output @@ -79,7 +79,7 @@ def cleanup(secret: str) -> None: output = GradingJobResult([], [e.with_traceback(None)]) push_results_to_response_url(output, grading_job["key"], - grading_job["container_response_url"] if - "container_response_url" in grading_job else + grading_job["container_response_url"] + if "container_response_url" in grading_job else grading_job["response_url"]) # cleanup(secret) # useful for execution with no container, but generally optional diff --git a/worker/orca_grader/container/grading_script/grading_script_command.py b/worker/orca_grader/container/grading_script/grading_script_command.py index f3fb009a..6ea69db6 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command.py +++ b/worker/orca_grader/container/grading_script/grading_script_command.py @@ -2,11 +2,12 @@ from orca_grader.common.grading_job.grading_job_result import GradingJobResult from orca_grader.container.grading_script.grading_script_command_response import GradingScriptCommandResponse + class GradingScriptCommand: - """ - Represents a single command in a grading script. Either executes a predicate/check - or a bash command. - """ + """ + Represents a single command in a grading script. Either executes a predicate/check + or a bash command. + """ - def execute(self, responses: List[GradingScriptCommandResponse]) -> GradingJobResult: - pass + def execute(self, responses: List[GradingScriptCommandResponse]) -> GradingJobResult: + pass diff --git a/worker/orca_grader/container/grading_script/grading_script_command_response.py b/worker/orca_grader/container/grading_script/grading_script_command_response.py index 878c24f5..0d1265aa 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command_response.py +++ b/worker/orca_grader/container/grading_script/grading_script_command_response.py @@ -1,52 +1,60 @@ -from typing import Dict, List +from typing import Dict, List, Optional + + +def __replace_paths_in_str(s: str, interpolated_dirs: Dict[str, str]) -> str: + result = s + for k, v in interpolated_dirs.items(): + result = s.replace(k, v) + return result + class GradingScriptCommandResponse: - """ - Response from the execution of a command when running a grading script. - Users can query if the response was an error, the output from the command, - the next place to go (i.e., next command | \"output\" | \"abort\"), and the - original command that was executed. - - Possibilities: - - isError() == true && (next == "abort" || next == "") - - isError() == false && (next == "output" || next == "") - """ - - def __init__(self, is_error: bool, cmd: List[str] | str, status_code: int, - stdout_output: str, stderr_output: str, timed_out: bool = False) -> None: - self.__is_error = is_error - self.__stdout_output = stdout_output - self.__stderr_output = stderr_output - self.__cmd = cmd - self.__status_code = status_code - self.__timed_out = timed_out - - def is_error(self) -> bool: - return self.__is_error - - def get_stdout_output(self) -> str: - return self.__stdout_output - - def get_stderr_output(self) -> str: - return self.__stderr_output - - def get_original_cmd(self) -> List[str] | str: - return self.__cmd - - def get_status_code(self) -> int: - return self.__status_code - - def did_time_out(self) -> bool: - return self.__timed_out - - # TODO: Replace with more accurate type. - def to_json(self) -> Dict[str, any]: - ans = { - "cmd": self.__cmd, - "stdout": self.__stdout_output, - "stderr": self.__stderr_output, - "is_error": self.__is_error, - "did_timeout": self.__timed_out, - "status_code": self.__status_code - } - return ans + """ + Response from the execution of a command when running a grading script. + Users can query if the response was an error, the output from the command, + the next place to go (i.e., next command | \"output\" | \"abort\"), and the + original command that was executed. + + Possibilities: + - isError() == true && (next == "abort" || next == "") + - isError() == false && (next == "output" || next == "") + """ + + def __init__(self, is_error: bool, cmd: List[str] | str, status_code: int, + stdout_output: str, stderr_output: str, timed_out: bool = False) -> None: + self.__is_error = is_error + self.__stdout_output = stdout_output + self.__stderr_output = stderr_output + self.__cmd = cmd + self.__status_code = status_code + self.__timed_out = timed_out + + def is_error(self) -> bool: + return self.__is_error + + def get_stdout_output(self) -> str: + return self.__stdout_output + + def get_stderr_output(self) -> str: + return self.__stderr_output + + def get_original_cmd(self) -> List[str] | str: + return self.__cmd + + def get_status_code(self) -> int: + return self.__status_code + + def did_time_out(self) -> bool: + return self.__timed_out + + # TODO: Replace with more accurate type. + def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> Dict[str, any]: + ans = { + "cmd": self.__cmd if interpolated_dirs is None else __replace_paths_in_str(self.__cmd, interpolated_dirs), + "stdout": self.__stdout_output if interpolated_dirs is None else __replace_paths_in_str(self.__stdout_output, interpolated_dirs), + "stderr": self.__stderr_output if interpolated_dirs is None else __replace_paths_in_str(self.__stderr_output, interpolated_dirs), + "is_error": self.__is_error, + "did_timeout": self.__timed_out, + "status_code": self.__status_code + } + return ans From c2a96e9c6eec2e8d07fc3f0683315e3cdea3e8d4 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Tue, 25 Jun 2024 20:48:28 -0400 Subject: [PATCH 02/11] qa: add tests for job result --- worker/orca_grader/__main__.py | 1 + .../common/services/push_results.py | 2 +- worker/orca_grader/container/do_grading.py | 4 +- .../grading_script_command_response.py | 1 - .../tests/common/test_grading_job_result.py | 37 ++++ worker/orca_grader/tests/runner.py | 173 +++++++++--------- 6 files changed, 133 insertions(+), 85 deletions(-) create mode 100644 worker/orca_grader/tests/common/test_grading_job_result.py diff --git a/worker/orca_grader/__main__.py b/worker/orca_grader/__main__.py index b2220742..2ac806b2 100644 --- a/worker/orca_grader/__main__.py +++ b/worker/orca_grader/__main__.py @@ -57,6 +57,7 @@ def process_jobs_from_db(no_container: bool, if job_retrieval_future.exception(): # TODO: replace with log statement. print(job_retrieval_future.exception()) + time.sleep(1) continue grading_job = job_retrieval_future.result() if grading_job is None: diff --git a/worker/orca_grader/common/services/push_results.py b/worker/orca_grader/common/services/push_results.py index 4e18c115..7d4b1a2d 100644 --- a/worker/orca_grader/common/services/push_results.py +++ b/worker/orca_grader/common/services/push_results.py @@ -15,7 +15,7 @@ def push_results_to_response_url(job_result: GradingJobResult, key: str, response_url: str, - interpolated_dirs: Optional[Dict[str, str]]) -> None: + interpolated_dirs: Optional[Dict[str, str]] = None) -> None: result_as_json = job_result.to_json(interpolated_dirs=interpolated_dirs) result_as_json["key"] = key _send_results_with_exponential_backoff(result_as_json, response_url) diff --git a/worker/orca_grader/container/do_grading.py b/worker/orca_grader/container/do_grading.py index 91252434..f081de51 100644 --- a/worker/orca_grader/container/do_grading.py +++ b/worker/orca_grader/container/do_grading.py @@ -45,11 +45,13 @@ def do_grading(secret: str, grading_job_json: GradingJobJSON) -> GradingJobResul except Exception as e: output = GradingJobResult(command_responses, [e]) print(output.to_json(interpolated_dirs=interpolated_dirs)) + reverse_interpolated_dirs = {v: k for k, v in interpolated_dirs.items()} push_results_to_response_url(output, grading_job_json["key"], grading_job_json["container_response_url"] if "container_response_url" in grading_job_json else - grading_job_json["response_url"]) + grading_job_json["response_url"], + interpolated_dirs=reverse_interpolated_dirs) return output diff --git a/worker/orca_grader/container/grading_script/grading_script_command_response.py b/worker/orca_grader/container/grading_script/grading_script_command_response.py index 0d1265aa..8675cfbc 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command_response.py +++ b/worker/orca_grader/container/grading_script/grading_script_command_response.py @@ -1,6 +1,5 @@ from typing import Dict, List, Optional - def __replace_paths_in_str(s: str, interpolated_dirs: Dict[str, str]) -> str: result = s for k, v in interpolated_dirs.items(): diff --git a/worker/orca_grader/tests/common/test_grading_job_result.py b/worker/orca_grader/tests/common/test_grading_job_result.py new file mode 100644 index 00000000..b6a945b4 --- /dev/null +++ b/worker/orca_grader/tests/common/test_grading_job_result.py @@ -0,0 +1,37 @@ +from unittest import TestCase +from orca_grader.common.grading_job.grading_job_result import GradingJobResult +from orca_grader.container.grading_script.grading_script_command_response import \ + GradingScriptCommandResponse + + +class TestGradingJobResult(TestCase): + + def setUp(self): + default_params = { + "is_error": False, + "cmd": ["echo", "hello"], + "stdout_output": "hello", + "stderr_output": "", + "status_code": 0, + } + self.__responses = [ + GradingScriptCommandResponse(**default_params), + GradingScriptCommandResponse( + **{ + **default_params, + "stdout_output": "build_dir/plus_more/file.txt" + } + ) + ] + self.__reversed_dirs = {"build_dir/plus_more": "$BUILD"} + + def test_result_without_interpolated_dirs(self): + result = GradingJobResult(command_responses=self.__responses) + second_response = result.to_json()[1] + self.assertFalse("$BUILD" in second_response) + + def test_result_with_interpolated_dirs(self): + result = GradingJobResult(command_responses=self.__responses) + second_response = result.to_json( + interpolated_dirs=self.__reversed_dirs)[1] + self.assertTrue("$BUILD" in second_response) diff --git a/worker/orca_grader/tests/runner.py b/worker/orca_grader/tests/runner.py index 5824ac5d..dca9c130 100644 --- a/worker/orca_grader/tests/runner.py +++ b/worker/orca_grader/tests/runner.py @@ -1,7 +1,6 @@ -from os import path +import sys import subprocess import time -from typing import List import unittest import shutil @@ -13,94 +12,104 @@ import orca_grader.tests.container.build_script.code_file.test_code_file_processor as test_code_file_processor import orca_grader.tests.docker_images.test_docker_image_loading as test_docker_image_loading import orca_grader.tests.container.build_script.preprocess.test_utils as test_preprocess_utils +import orca_grader.tests.common.test_grading_job_result as test_grading_job_result + +from os import path -__TIME_FOR_FILE_SERVER_STARTUP = 2 # seconds +__TIME_FOR_FILE_SERVER_STARTUP = 2 # seconds __FIXTURE_DIRS_TO_COPY = ["code_files", "images"] + def __copy_fixtures_to_test_server() -> None: - for dir in __FIXTURE_DIRS_TO_COPY: - shutil.copytree(path.join("orca_grader/tests/fixtures", dir), - path.join("images/testing/simple-server/files", dir), dirs_exist_ok=True) - + for dir in __FIXTURE_DIRS_TO_COPY: + shutil.copytree(path.join("orca_grader/tests/fixtures", dir), + path.join("images/testing/simple-server/files", dir), dirs_exist_ok=True) + + def __rm_fixtures_from_test_server() -> None: - for dir in __FIXTURE_DIRS_TO_COPY: - shutil.rmtree(path.join("images/testing/simple-server/files", dir)) + for dir in __FIXTURE_DIRS_TO_COPY: + shutil.rmtree(path.join("images/testing/simple-server/files", dir)) + def __start_up_fixture_file_server(): - __copy_fixtures_to_test_server() - subprocess.run( - [ - "docker", - "build", - "images/testing/simple-server", - "-f", - "images/testing/simple-server/Dockerfile", - "-t", - "simple-server" - ], - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, - check=True - ) - subprocess.run( - [ - "docker", - "run", - "--rm", - "-d", - "-p", - "9000:9000", - "--name", - "simple-server", - "simple-server" - ], - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, - check=True - ) - time.sleep(__TIME_FOR_FILE_SERVER_STARTUP) + __copy_fixtures_to_test_server() + subprocess.run( + [ + "docker", + "build", + "images/testing/simple-server", + "-f", + "images/testing/simple-server/Dockerfile", + "-t", + "simple-server" + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + check=True + ) + subprocess.run( + [ + "docker", + "run", + "--rm", + "-d", + "-p", + "9000:9000", + "--name", + "simple-server", + "simple-server" + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + check=True + ) + time.sleep(__TIME_FOR_FILE_SERVER_STARTUP) + def __clean_up_fixture_file_server(): - subprocess.run( - [ - "docker", - "stop", - "-t", - "0", - "simple-server" - ], - stdout=subprocess.DEVNULL, - stderr=subprocess.STDOUT, - check=True - ) - __rm_fixtures_from_test_server() + subprocess.run( + [ + "docker", + "stop", + "-t", + "0", + "simple-server" + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT, + check=True + ) + __rm_fixtures_from_test_server() + if __name__ == '__main__': - try: - print("Spinning up local file server for testing...") - __start_up_fixture_file_server() - print("Local file server started.") - except subprocess.CalledProcessError as called_proc_err: - print("Could not start up local file server for testing.\n") - exit(1) - loader = unittest.TestLoader() - suite = unittest.TestSuite() - # Load Test Cases - suite.addTests(loader.loadTestsFromModule(test_cycle_detector)) - suite.addTests(loader.loadTestsFromModule(test_bash_grading_script_command)) - suite.addTests(loader.loadTestsFromModule(test_conditional_grading_script_command)) - suite.addTests(loader.loadTestsFromModule(test_code_file_info)) - suite.addTests(loader.loadTestsFromModule(test_code_file_processor)) - suite.addTests(loader.loadTestsFromModule(test_docker_image_loading)) - suite.addTests(loader.loadTestsFromModule(test_preprocess_utils)) - runner = unittest.TextTestRunner(verbosity=3) - result = runner.run(suite) - try: - print("Cleaning up test server...") - __clean_up_fixture_file_server() - print("Clean up complete.") - exit(0 if result.wasSuccessful() else 1) - except subprocess.CalledProcessError as called_proc_err: - print("Could not successfully clean up testing server.") - exit(1) - \ No newline at end of file + try: + sys.stderr.write("Spinning up local file server for testing...") + __start_up_fixture_file_server() + sys.stderr.write("Local file server started.") + except subprocess.CalledProcessError as called_proc_err: + sys.stderr.write("Could not start up local file server for testing.\n") + exit(1) + loader = unittest.TestLoader() + suite = unittest.TestSuite() + # Load Test Cases + suite.addTests(loader.loadTestsFromModule(test_cycle_detector)) + suite.addTests(loader.loadTestsFromModule( + test_bash_grading_script_command)) + suite.addTests(loader.loadTestsFromModule( + test_conditional_grading_script_command)) + suite.addTests(loader.loadTestsFromModule(test_code_file_info)) + suite.addTests(loader.loadTestsFromModule(test_code_file_processor)) + suite.addTests(loader.loadTestsFromModule(test_docker_image_loading)) + suite.addTests(loader.loadTestsFromModule(test_preprocess_utils)) + suite.addTests(loader.loadTestsFromModule(test_grading_job_result)) + runner = unittest.TextTestRunner(verbosity=3) + result = runner.run(suite) + try: + sys.stderr.write("Cleaning up test server...") + __clean_up_fixture_file_server() + sys.stderr.write("Clean up complete.") + exit(0 if result.wasSuccessful() else 1) + except subprocess.CalledProcessError as called_proc_err: + sys.stderr.write("Could not successfully clean up testing server.") + exit(1) From 666ee1e363af0b1421da1af8b92096a3d8f837f9 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Tue, 25 Jun 2024 20:50:42 -0400 Subject: [PATCH 03/11] fix: bad key on test --- worker/orca_grader/tests/common/test_grading_job_result.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/worker/orca_grader/tests/common/test_grading_job_result.py b/worker/orca_grader/tests/common/test_grading_job_result.py index b6a945b4..91915c38 100644 --- a/worker/orca_grader/tests/common/test_grading_job_result.py +++ b/worker/orca_grader/tests/common/test_grading_job_result.py @@ -27,11 +27,10 @@ def setUp(self): def test_result_without_interpolated_dirs(self): result = GradingJobResult(command_responses=self.__responses) - second_response = result.to_json()[1] + second_response = result.to_json()["shell_responses"][1] self.assertFalse("$BUILD" in second_response) def test_result_with_interpolated_dirs(self): result = GradingJobResult(command_responses=self.__responses) - second_response = result.to_json( - interpolated_dirs=self.__reversed_dirs)[1] + second_response = result.to_json(interpolated_dirs=self.__reversed_dirs)["shell_responses"][1] self.assertTrue("$BUILD" in second_response) From 1c2b310bc81e543d8bf4375b9bc74d95adc6c19a Mon Sep 17 00:00:00 2001 From: williams-jack Date: Tue, 25 Jun 2024 20:58:18 -0400 Subject: [PATCH 04/11] chore: test ordering of args --- .../grading_script/grading_script_command_response.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worker/orca_grader/container/grading_script/grading_script_command_response.py b/worker/orca_grader/container/grading_script/grading_script_command_response.py index 8675cfbc..213bf650 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command_response.py +++ b/worker/orca_grader/container/grading_script/grading_script_command_response.py @@ -1,5 +1,6 @@ from typing import Dict, List, Optional + def __replace_paths_in_str(s: str, interpolated_dirs: Dict[str, str]) -> str: result = s for k, v in interpolated_dirs.items(): @@ -49,7 +50,7 @@ def did_time_out(self) -> bool: # TODO: Replace with more accurate type. def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> Dict[str, any]: ans = { - "cmd": self.__cmd if interpolated_dirs is None else __replace_paths_in_str(self.__cmd, interpolated_dirs), + "cmd": __replace_paths_in_str(self.__cmd, interpolated_dirs) if interpolated_dirs is not None else self.__cmd, "stdout": self.__stdout_output if interpolated_dirs is None else __replace_paths_in_str(self.__stdout_output, interpolated_dirs), "stderr": self.__stderr_output if interpolated_dirs is None else __replace_paths_in_str(self.__stderr_output, interpolated_dirs), "is_error": self.__is_error, From 13d53047f03c9af10ac456a830d441dc49830a65 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Tue, 25 Jun 2024 21:16:10 -0400 Subject: [PATCH 05/11] fix: bath keys in test --- .../grading_script_command_response.py | 15 +++++++++------ .../tests/common/test_grading_job_result.py | 7 ++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/worker/orca_grader/container/grading_script/grading_script_command_response.py b/worker/orca_grader/container/grading_script/grading_script_command_response.py index 213bf650..283ec19b 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command_response.py +++ b/worker/orca_grader/container/grading_script/grading_script_command_response.py @@ -1,10 +1,13 @@ from typing import Dict, List, Optional -def __replace_paths_in_str(s: str, interpolated_dirs: Dict[str, str]) -> str: - result = s +def replace_paths_in_str(str_or_list: str | List[str], interpolated_dirs: Dict[str, str]) -> str: + result = str_or_list for k, v in interpolated_dirs.items(): - result = s.replace(k, v) + if type(result) is str: + result = str_or_list.replace(k, v) + else: + result = [s.replace(k, v) for s in result] return result @@ -50,9 +53,9 @@ def did_time_out(self) -> bool: # TODO: Replace with more accurate type. def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> Dict[str, any]: ans = { - "cmd": __replace_paths_in_str(self.__cmd, interpolated_dirs) if interpolated_dirs is not None else self.__cmd, - "stdout": self.__stdout_output if interpolated_dirs is None else __replace_paths_in_str(self.__stdout_output, interpolated_dirs), - "stderr": self.__stderr_output if interpolated_dirs is None else __replace_paths_in_str(self.__stderr_output, interpolated_dirs), + "cmd": self.__cmd if interpolated_dirs is None else replace_paths_in_str(self.__cmd, interpolated_dirs), + "stdout": self.__stdout_output if interpolated_dirs is None else replace_paths_in_str(self.__stdout_output, interpolated_dirs), + "stderr": self.__stderr_output if interpolated_dirs is None else replace_paths_in_str(self.__stderr_output, interpolated_dirs), "is_error": self.__is_error, "did_timeout": self.__timed_out, "status_code": self.__status_code diff --git a/worker/orca_grader/tests/common/test_grading_job_result.py b/worker/orca_grader/tests/common/test_grading_job_result.py index 91915c38..8ff2c095 100644 --- a/worker/orca_grader/tests/common/test_grading_job_result.py +++ b/worker/orca_grader/tests/common/test_grading_job_result.py @@ -1,4 +1,4 @@ -from unittest import TestCase +from unittest import TestCase, main from orca_grader.common.grading_job.grading_job_result import GradingJobResult from orca_grader.container.grading_script.grading_script_command_response import \ GradingScriptCommandResponse @@ -27,10 +27,11 @@ def setUp(self): def test_result_without_interpolated_dirs(self): result = GradingJobResult(command_responses=self.__responses) - second_response = result.to_json()["shell_responses"][1] + second_response = result.to_json()["shell_responses"][1]["stdout"] self.assertFalse("$BUILD" in second_response) def test_result_with_interpolated_dirs(self): result = GradingJobResult(command_responses=self.__responses) - second_response = result.to_json(interpolated_dirs=self.__reversed_dirs)["shell_responses"][1] + second_response = result.to_json(interpolated_dirs=self.__reversed_dirs)[ + "shell_responses"][1]["stdout"] self.assertTrue("$BUILD" in second_response) From 50a57f371443bf45f8b0a0f7871267de98cb5ff9 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Sat, 29 Jun 2024 14:13:12 -0400 Subject: [PATCH 06/11] fix: always included some notion of path conversion --- .../common/grading_job/grading_job_result.py | 2 +- .../orca_grader/common/services/push_results.py | 10 ++++++---- worker/orca_grader/container/do_grading.py | 3 ++- .../grading_script_command_response.py | 16 +++++----------- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/worker/orca_grader/common/grading_job/grading_job_result.py b/worker/orca_grader/common/grading_job/grading_job_result.py index b76d8fb1..33d4b14d 100644 --- a/worker/orca_grader/common/grading_job/grading_job_result.py +++ b/worker/orca_grader/common/grading_job/grading_job_result.py @@ -21,7 +21,7 @@ def get_output(self) -> Optional[str]: def get_execution_errors(self) -> List[Exception]: return self.__execution_errors - def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> GradingJobOutputJSON: + def to_json(self, interpolated_dirs: Dict[str, str]) -> GradingJobOutputJSON: result = dict() json_responses = list( map( diff --git a/worker/orca_grader/common/services/push_results.py b/worker/orca_grader/common/services/push_results.py index 7d4b1a2d..a2720e0f 100644 --- a/worker/orca_grader/common/services/push_results.py +++ b/worker/orca_grader/common/services/push_results.py @@ -15,9 +15,11 @@ def push_results_to_response_url(job_result: GradingJobResult, key: str, response_url: str, - interpolated_dirs: Optional[Dict[str, str]] = None) -> None: - result_as_json = job_result.to_json(interpolated_dirs=interpolated_dirs) - result_as_json["key"] = key + interpolated_dirs: Dict[str, str]) -> None: + result_as_json = { + **job_result.to_json(interpolated_dirs=interpolated_dirs), + "key": key + } _send_results_with_exponential_backoff(result_as_json, response_url) @@ -25,7 +27,7 @@ def push_results_with_exception(grading_job: GradingJobJSON, e: Exception) -> None: output = GradingJobResult([], [e]).to_json() key, response_url = grading_job["key"], grading_job["response_url"] - push_results_to_response_url(output, key, response_url) + push_results_to_response_url(output, key, response_url, {}) def _send_results_with_exponential_backoff(payload: dict, response_url: str, n: int = 1): diff --git a/worker/orca_grader/container/do_grading.py b/worker/orca_grader/container/do_grading.py index f081de51..7bb9ffc4 100644 --- a/worker/orca_grader/container/do_grading.py +++ b/worker/orca_grader/container/do_grading.py @@ -83,5 +83,6 @@ def cleanup(secret: str) -> None: grading_job["key"], grading_job["container_response_url"] if "container_response_url" in grading_job else - grading_job["response_url"]) + grading_job["response_url"], + interpolated_dirs={}) # cleanup(secret) # useful for execution with no container, but generally optional diff --git a/worker/orca_grader/container/grading_script/grading_script_command_response.py b/worker/orca_grader/container/grading_script/grading_script_command_response.py index 283ec19b..46c51910 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command_response.py +++ b/worker/orca_grader/container/grading_script/grading_script_command_response.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Optional +from typing import Dict, List def replace_paths_in_str(str_or_list: str | List[str], interpolated_dirs: Dict[str, str]) -> str: @@ -14,13 +14,8 @@ def replace_paths_in_str(str_or_list: str | List[str], interpolated_dirs: Dict[s class GradingScriptCommandResponse: """ Response from the execution of a command when running a grading script. - Users can query if the response was an error, the output from the command, - the next place to go (i.e., next command | \"output\" | \"abort\"), and the - original command that was executed. - - Possibilities: - - isError() == true && (next == "abort" || next == "") - - isError() == false && (next == "output" || next == "") + Exposes methods to query if the response was an error, the output from the command, + and the original command that was executed. """ def __init__(self, is_error: bool, cmd: List[str] | str, status_code: int, @@ -51,8 +46,8 @@ def did_time_out(self) -> bool: return self.__timed_out # TODO: Replace with more accurate type. - def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> Dict[str, any]: - ans = { + def to_json(self, interpolated_dirs: Dict[str, str]) -> Dict[str, any]: + return { "cmd": self.__cmd if interpolated_dirs is None else replace_paths_in_str(self.__cmd, interpolated_dirs), "stdout": self.__stdout_output if interpolated_dirs is None else replace_paths_in_str(self.__stdout_output, interpolated_dirs), "stderr": self.__stderr_output if interpolated_dirs is None else replace_paths_in_str(self.__stderr_output, interpolated_dirs), @@ -60,4 +55,3 @@ def to_json(self, interpolated_dirs: Optional[Dict[str, str]] = None) -> Dict[st "did_timeout": self.__timed_out, "status_code": self.__status_code } - return ans From 900a241eaccdde224688c9897abaee74ebf2f467 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Sat, 29 Jun 2024 14:16:59 -0400 Subject: [PATCH 07/11] fix: update missing interpolated dirs param --- worker/orca_grader/tests/common/test_grading_job_result.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/orca_grader/tests/common/test_grading_job_result.py b/worker/orca_grader/tests/common/test_grading_job_result.py index 8ff2c095..c5c85309 100644 --- a/worker/orca_grader/tests/common/test_grading_job_result.py +++ b/worker/orca_grader/tests/common/test_grading_job_result.py @@ -27,7 +27,7 @@ def setUp(self): def test_result_without_interpolated_dirs(self): result = GradingJobResult(command_responses=self.__responses) - second_response = result.to_json()["shell_responses"][1]["stdout"] + second_response = result.to_json(interpolated_dirs={})["shell_responses"][1]["stdout"] self.assertFalse("$BUILD" in second_response) def test_result_with_interpolated_dirs(self): From b147f2545967b15d99d9995a7189419aa73e8948 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Fri, 12 Jul 2024 15:23:26 -0400 Subject: [PATCH 08/11] fix: logging of stderr and stdout swapped; command response did not use accumulated var --- worker/orca_grader/__main__.py | 4 ++-- .../grading_script/grading_script_command_response.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/worker/orca_grader/__main__.py b/worker/orca_grader/__main__.py index 2ac806b2..8bb3fc37 100644 --- a/worker/orca_grader/__main__.py +++ b/worker/orca_grader/__main__.py @@ -150,9 +150,9 @@ def handle_grading_job(grading_job: GradingJobJSON, container_sha: str | None = result = executor.execute() if result and result.stdout: # TODO: make this a log statement of some sort. - print(result.stderr.decode()) - elif result and result.stderr: print(result.stdout.decode()) + elif result and result.stderr: + print(result.stderr.decode()) def can_execute_job(grading_job: GradingJobJSON) -> bool: diff --git a/worker/orca_grader/container/grading_script/grading_script_command_response.py b/worker/orca_grader/container/grading_script/grading_script_command_response.py index 46c51910..7fd064aa 100644 --- a/worker/orca_grader/container/grading_script/grading_script_command_response.py +++ b/worker/orca_grader/container/grading_script/grading_script_command_response.py @@ -5,7 +5,7 @@ def replace_paths_in_str(str_or_list: str | List[str], interpolated_dirs: Dict[s result = str_or_list for k, v in interpolated_dirs.items(): if type(result) is str: - result = str_or_list.replace(k, v) + result = result.replace(k, v) else: result = [s.replace(k, v) for s in result] return result @@ -48,9 +48,9 @@ def did_time_out(self) -> bool: # TODO: Replace with more accurate type. def to_json(self, interpolated_dirs: Dict[str, str]) -> Dict[str, any]: return { - "cmd": self.__cmd if interpolated_dirs is None else replace_paths_in_str(self.__cmd, interpolated_dirs), - "stdout": self.__stdout_output if interpolated_dirs is None else replace_paths_in_str(self.__stdout_output, interpolated_dirs), - "stderr": self.__stderr_output if interpolated_dirs is None else replace_paths_in_str(self.__stderr_output, interpolated_dirs), + "cmd": replace_paths_in_str(self.__cmd, interpolated_dirs), + "stdout": replace_paths_in_str(self.__stdout_output, interpolated_dirs), + "stderr": replace_paths_in_str(self.__stderr_output, interpolated_dirs), "is_error": self.__is_error, "did_timeout": self.__timed_out, "status_code": self.__status_code From 43fad95bb86684130c37323cc76f1114ad7d71cd Mon Sep 17 00:00:00 2001 From: williams-jack Date: Sun, 14 Jul 2024 11:18:08 -0400 Subject: [PATCH 09/11] fix: subscripts for on_* keys are lists of commands, not a single one --- .../validations/schemas/grading-job-config.ts | 10 +++++++-- .../bash_grading_script_command_schema.json | 22 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts b/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts index 8ed86c7a..41f60633 100644 --- a/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts +++ b/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts @@ -56,7 +56,10 @@ const conditionalGradingScriptCommand = { on_true: { anyOf: [ { - $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + type: "array", + items: { + $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + } }, { type: "string" }, { type: "number" }, @@ -65,7 +68,10 @@ const conditionalGradingScriptCommand = { on_false: { anyOf: [ { - $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + type: "array", + items: { + $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + } }, { type: "string" }, { type: "number" }, diff --git a/worker/orca_grader/validations/schemas/bash_grading_script_command_schema.json b/worker/orca_grader/validations/schemas/bash_grading_script_command_schema.json index 1222b7e2..1e7f0a92 100644 --- a/worker/orca_grader/validations/schemas/bash_grading_script_command_schema.json +++ b/worker/orca_grader/validations/schemas/bash_grading_script_command_schema.json @@ -19,16 +19,30 @@ "oneOf": [ { "type": "string" }, { "type": "number" }, - { "$ref": "bash_grading_script_command_schema.json" }, - { "$ref": "conditional_grading_script_comamnd_schema.json" } + { + "type": "array", + "items": { + "oneOf": [ + { "$ref": "bash_grading_script_command_schema.json" }, + { "$ref": "conditional_grading_script_comamnd_schema.json" } + ] + } + } ] }, "on_fail": { "oneOf": [ { "type": "string" }, { "type": "number" }, - { "$ref": "bash_grading_script_command_schema.json" }, - { "$ref": "conditional_grading_script_comamnd_schema.json" } + { + "type": "array", + "items": { + "oneOf": [ + { "$ref": "bash_grading_script_command_schema.json" }, + { "$ref": "conditional_grading_script_comamnd_schema.json" } + ] + } + } ] }, "timeout": { "type": "number" }, From 38e9bddc5c0830e4a002e09b3d0bd0138019288a Mon Sep 17 00:00:00 2001 From: williams-jack Date: Sun, 14 Jul 2024 22:33:14 -0400 Subject: [PATCH 10/11] refactor: compatible db port for local dev; fix schema for list of command --- .../src/validations/schemas/grading-job-config.ts | 10 ++++++++-- orchestrator/packages/db/run-migration | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts b/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts index 41f60633..b9d82622 100644 --- a/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts +++ b/orchestrator/packages/common/src/validations/schemas/grading-job-config.ts @@ -18,7 +18,10 @@ const bashGradingScriptCommand = { on_complete: { anyOf: [ { - $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + type: "array", + items: { + $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + } }, { type: "string" }, { type: "number" }, @@ -27,7 +30,10 @@ const bashGradingScriptCommand = { on_fail: { anyOf: [ { - $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + type: "array", + items: { + $ref: "https://orca-schemas.com/grading-job-config/grading-script-command", + } }, { type: "string" }, { type: "number" }, diff --git a/orchestrator/packages/db/run-migration b/orchestrator/packages/db/run-migration index 87dc9ee8..1bbf15a8 100755 --- a/orchestrator/packages/db/run-migration +++ b/orchestrator/packages/db/run-migration @@ -8,7 +8,7 @@ fi migration_name="$1" container_name='postgres-migration' -docker run --rm -p 5432:5432 -v db-data:/var/lib/postgresql/data \ +docker run --rm -p 5434:5432 -v db-data:/var/lib/postgresql/data \ -e POSTGRES_PASSWORD=password --name "$container_name" -d postgres:10 > /dev/null if [[ $? -ne 0 ]]; then @@ -16,6 +16,6 @@ if [[ $? -ne 0 ]]; then exit 1 fi -export POSTGRES_URL=postgresql://postgres:password@localhost +export POSTGRES_URL=postgresql://postgres:password@localhost:5434 npx prisma migrate dev --name $1 docker stop "$container_name" > /dev/null From 3c7d80ecd395e88d790cd46c5b93e6d78de55a32 Mon Sep 17 00:00:00 2001 From: williams-jack Date: Sun, 14 Jul 2024 22:34:46 -0400 Subject: [PATCH 11/11] chore: new lines for better worker test logs --- worker/orca_grader/tests/runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worker/orca_grader/tests/runner.py b/worker/orca_grader/tests/runner.py index dca9c130..e03cb8a4 100644 --- a/worker/orca_grader/tests/runner.py +++ b/worker/orca_grader/tests/runner.py @@ -84,9 +84,9 @@ def __clean_up_fixture_file_server(): if __name__ == '__main__': try: - sys.stderr.write("Spinning up local file server for testing...") + sys.stderr.write("Spinning up local file server for testing...\n") __start_up_fixture_file_server() - sys.stderr.write("Local file server started.") + sys.stderr.write("Local file server started.\n") except subprocess.CalledProcessError as called_proc_err: sys.stderr.write("Could not start up local file server for testing.\n") exit(1)