From 3a1fbddb5ccb1808f64e5b16dbe70144e2e18b21 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 11:03:45 -0500 Subject: [PATCH 001/235] Update README doc --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3369e432..c404fb3a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Ridgeback -API for Toil LSF +API for running Toil jobs. +Supports LSF, SLURM, and singleMachine mode ## Filebeat + See https://app.gitbook.com/@mskcc-1/s/experimental-dev/filebeat From 0c9531814698cf995662a8151dd54ec3f56cdc0b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 11:04:48 -0500 Subject: [PATCH 002/235] Add factory class for batch system clients --- batch_systems/__init__.py | 4 ++ batch_systems/batch_system.py | 120 ++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 batch_systems/__init__.py create mode 100644 batch_systems/batch_system.py diff --git a/batch_systems/__init__.py b/batch_systems/__init__.py new file mode 100644 index 00000000..4d59af0d --- /dev/null +++ b/batch_systems/__init__.py @@ -0,0 +1,4 @@ +from .lsf_client.lsf_client import LSFClient +from .slurm_client.slurm_client import SLURMClient + +__all__ = ["LSFClient", "SLURMClient"] diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py new file mode 100644 index 00000000..e2b5fd45 --- /dev/null +++ b/batch_systems/batch_system.py @@ -0,0 +1,120 @@ +from django.conf import settings +import logging +from batch_systems.lsf_client.lsf_client import LSFClient +from batch_systems.slurm_client.slurm_client import SLURMClient + + +def get_batch_system(): + if settings.BATCH_SYSTEM == "LSF": + return LSFClient() + elif settings.BATCH_SYSTEM == "SLURM": + return SLURMClient() + else: + raise Exception(f"Batch system {settings.BATCH_SYSTEM} not supported, please use either LSF or SLURM") + + +class BatchClient(object): + """ + Client for a generic Batch system + + Attributes: + logger (logging): logging module + """ + + def __init__(self): + """ + init function + """ + self.logger = logging.getLogger("BATCH_client") + self.logfileName = "batch.log" + self.name = "batch" + + def submit(self, command, job_args, stdout, job_id, env={}): + """ + Submit command to LSF and store log in stdout + + Args: + command (str): command to submit + job_args (list): Additional options for leader job + stdout (str): log file path + job_id (str): job_id + env (dict): Environment variables + + Returns: + int: batch job id + """ + + def terminate(self, job_id): + """ + Kill Batch job + + Args: + job_id (str): job_id + + Returns: + bool: successful + """ + + def set_walltime(self, expected_limit, hard_limit): + """ + Set the walltime args of the batch job + """ + walltime_args = [] + return walltime_args + + def set_memlimit(self, mem_limit, default=None): + """ + Set the memlimit args of the batch job + """ + mem_limit_args = [] + return mem_limit_args + + def set_group(self, group_id): + """ + Set the group args of the batch job + """ + group_id_args = [] + return group_id_args + + def set_stdout_file(self, stdout_file): + """ + Set the output path of the log file + """ + return [] + + def set_service_queue(self): + """ + Set the service queue parameter + """ + service_queue_args = [] + return service_queue_args + + def status(self, external_job_id): + """Parse Batch status + + Args: + external_job_id (str): Batch id + + Returns: + tuple: (Ridgeback Status int, extra info) + """ + status = None + return status + + def suspend(self, job_id): + """ + Suspend Batch job + Args: + job_id (str): id of job + Returns: + bool: successful + """ + + def resume(self, job_id): + """ + Resume Batch job + Args: + job_id (str): id of job + Returns: + bool: successful + """ From 60c56bc4442c1abe1e50eeb21b0d0d34b904dfea Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 11:06:46 -0500 Subject: [PATCH 003/235] Use factory method for LSF client --- batch_systems/lsf_client/lsf_client.py | 54 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index d2702f9a..ed36ed96 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -1,6 +1,7 @@ """ Submit, monitor, and control LSF jobs """ + import os import re import subprocess @@ -9,13 +10,14 @@ from django.conf import settings from orchestrator.models import Status from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException +from batch_systems.batch_system import BatchClient def format_lsf_job_id(job_id): return "/{}".format(job_id) -class LSFClient(object): +class LSFClient(BatchClient): """ Client for LSF @@ -28,6 +30,8 @@ def __init__(self): init function """ self.logger = logging.getLogger("LSF_client") + self.logfileName = "lsf.log" + self.name = "lsf" def submit(self, command, job_args, stdout, job_id, env={}): """ @@ -43,11 +47,9 @@ def submit(self, command, job_args, stdout, job_id, env={}): Returns: int: lsf job id """ - if settings.LSF_SLA: - bsub_command = ["bsub", "-sla", settings.LSF_SLA, "-g", format_lsf_job_id(job_id), "-oo", stdout] + job_args - else: - bsub_command = ["bsub", "-g", format_lsf_job_id(job_id), "-oo", stdout] + job_args - + bsub_command = ( + ["bsub"] + self.set_service_queue() + self.set_group(job_id) + self.set_stdout_file(stdout) + job_args + ) bsub_command.extend(command) current_env = os.environ.copy() for k, v in env.items(): @@ -87,7 +89,41 @@ def terminate(self, job_id): return True return False - def parse_bjobs(self, bjobs_output_str): + def set_walltime(self, expected_limit, hard_limit): + walltime_args = [] + if expected_limit: + walltime_args = walltime_args + ["-We", str(expected_limit)] + if hard_limit: + walltime_args = walltime_args + ["-W", str(hard_limit)] + return walltime_args + + def set_memlimit(self, mem_limit, default=None): + mem_limit_args = [] + if default: + mem_limit = ["-M", default] + if mem_limit: + mem_limit_args = ["-M", mem_limit] + return mem_limit_args + + def set_group(self, group_id): + group_id_args = [] + if group_id: + group_id_args = ["-g", format_lsf_job_id(group_id)] + return group_id_args + + def set_stdout_file(self, stdout_file): + if stdout_file: + return ["-oo", stdout_file] + else: + return ["-oo", self.logfileName] + + def set_service_queue(self): + service_queue_args = [] + if settings.LSF_SLA: + service_queue_args = ["-sla", settings.LSF_SLA] + return service_queue_args + + def _parse_bjobs(self, bjobs_output_str): """ Parse the output of bjobs into a descriptive dict @@ -190,7 +226,7 @@ def _parse_status(self, stdout, external_job_id): Returns: tuple: (Ridgeback Status int, extra info) """ - bjobs_records = self.parse_bjobs(stdout) + bjobs_records = self._parse_bjobs(stdout) if bjobs_records: process_output = bjobs_records[0] if "STAT" in process_output: @@ -228,7 +264,7 @@ def suspend(self, job_id): """ Suspend LSF job Args: - extrnsl_job_id (str): id of job + job_id (str): id of job Returns: bool: successful """ From 203b716a5fe62937a2c1d3fd90ea29e6bb976fe8 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 12:37:04 -0500 Subject: [PATCH 004/235] Removed references to lSF --- orchestrator/commands/command.py | 2 +- orchestrator/tasks.py | 58 +++++++++++++++----------------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/orchestrator/commands/command.py b/orchestrator/commands/command.py index c621c57b..f54cc6cd 100644 --- a/orchestrator/commands/command.py +++ b/orchestrator/commands/command.py @@ -3,7 +3,7 @@ class CommandType(IntEnum): - CHECK_STATUS_ON_LSF = 0 + CHECK_STATUS_ON_BATCH_SYSTEM = 0 CHECK_COMMAND_LINE_STATUS = 1 PREPARE = 2 SUBMIT = 3 diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 4aa3036f..828e7684 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -13,7 +13,7 @@ from submitter.factory import JobSubmitterFactory from orchestrator.scheduler import Scheduler from orchestrator.commands import Command, CommandType -from batch_systems.lsf_client.lsf_client import LSFClient +from batch_systems.batch_system import get_batch_system from orchestrator.exceptions import ( RetryException, StopException, @@ -22,6 +22,7 @@ logger = logging.getLogger(__name__) +BATCH_SYSTEM = get_batch_system() def get_job_info_path(job_id): @@ -54,8 +55,7 @@ def save_job_info(job_id, external_id, job_store_location, working_dir, output_d def suspend_job(job): if Status(job.status).transition(Status.SUSPENDED): - lsf_client = LSFClient() - job_suspended = lsf_client.suspend(str(job.id)) + job_suspended = BATCH_SYSTEM.suspend(str(job.id)) if not job_suspended: raise RetryException("Failed to suspend job: %s" % str(job.id)) job.update_status(Status.SUSPENDED) @@ -74,8 +74,7 @@ def resume_job(job): log_dir=job.log_dir, app_name=job.metadata["pipeline_name"], ) - lsf_client = LSFClient() - job_resumed = lsf_client.resume(submitter.job_id) + job_resumed = BATCH_SYSTEM.resume(submitter.job_id) if not job_resumed: raise RetryException("Failed to resume job: %s" % str(job.id)) job.update_status(Status.RUNNING) @@ -106,7 +105,7 @@ def process_jobs(): for job_id in status_jobs: # Send CHECK_STATUS commands for Jobs - command_processor.delay(Command(CommandType.CHECK_STATUS_ON_LSF, str(job_id)).to_dict()) + command_processor.delay(Command(CommandType.CHECK_STATUS_ON_BATCH_SYSTEM, str(job_id)).to_dict()) jobs = Scheduler.get_jobs_to_submit() @@ -133,10 +132,10 @@ def command_processor(self, command_dict): logger.debug("PREPARE command for job %s" % command.job_id) prepare_job(job) elif command.command_type == CommandType.SUBMIT: - logger.debug("SUBMIT command for job %s" % command.job_id) - submit_job_to_lsf(job, self.request.retries) - elif command.command_type == CommandType.CHECK_STATUS_ON_LSF: - logger.debug("CHECK_STATUS_ON_LSF command for job %s" % command.job_id) + logger.info("SUBMIT command for job %s" % command.job_id) + submit_job_to_batch_system(job, self.request.retries) + elif command.command_type == CommandType.CHECK_STATUS_ON_BATCH_SYSTEM: + logger.info("CHECK_STATUS_ON_BATCH_SYSTEM command for job %s" % command.job_id) check_job_status(job) elif command.command_type == CommandType.CHECK_COMMAND_LINE_STATUS: logger.debug("CHECK_COMMAND_LINE_STATUS command for job %s" % command.job_id) @@ -212,10 +211,9 @@ def prepare_job(job): job.job_prepared(job_store_dir, job_work_dir, job_output_dir, log_dir) -def submit_job_to_lsf(job, retries=0): +def submit_job_to_batch_system(job, retries=0): if Status(job.status).transition(Status.SUBMITTED): - logger.info(f"Submitting job {str(job.id)} to lsf. Try {retries}") - lsf_client = LSFClient() + logger.info(f"Submitting job {str(job.id)} to {BATCH_SYSTEM.name}. Try {retries}") submitter = JobSubmitterFactory.factory( job.type, str(job.id), @@ -231,7 +229,7 @@ def submit_job_to_lsf(job, retries=0): ) try: command_line, args, log_path, job_id, env = submitter.get_submit_command() - external_job_id = lsf_client.submit(command_line, args, log_path, job_id, env) + external_job_id = BATCH_SYSTEM.submit(command_line, args, log_path, job_id, env) except Exception as f: if retries < 5: logger.exception(str(f)) @@ -240,7 +238,7 @@ def submit_job_to_lsf(job, retries=0): logger.exception(str(f)) raise StopException(f"Failed to submit job to scheduler {str(job.id)} no more retries") else: - logger.info(f"Job {str(job.id)} submitted to lsf with id: {external_job_id}") + logger.info(f"Job {str(job.id)} submitted to {BATCH_SYSTEM.name} with id: {external_job_id}") job.submitted_to_scheduler(external_job_id) # Keeping this for debugging purposes save_job_info( @@ -304,26 +302,25 @@ def check_job_status(job): ): return try: - lsf_client = LSFClient() - lsf_status, lsf_message = lsf_client.status(str(job.external_id)) + batch_system_status, batch_system_message = BATCH_SYSTEM.status(str(job.external_id)) except FetchStatusException as e: - # If failed to check status on LSF retry + # If failed to check status on batch system retry logger.exception(e) raise RetryException("Failed to fetch status for job %s" % (str(job.id))) - if Status(job.status).transition(lsf_status): - if lsf_status in ( + if Status(job.status).transition(batch_system_status): + if batch_system_status in ( Status.SUBMITTED, Status.PENDING, Status.RUNNING, Status.UNKNOWN, ): - job.update_status(lsf_status) + job.update_status(batch_system_status) - if lsf_status in (Status.RUNNING,): + if batch_system_status in (Status.RUNNING,): command_processor.delay(Command(CommandType.CHECK_HANGING, str(job.id)).to_dict()) command_processor.delay(Command(CommandType.CHECK_COMMAND_LINE_STATUS, str(job.id)).to_dict()) - elif lsf_status in (Status.COMPLETED,): + elif batch_system_status in (Status.COMPLETED,): submitter = JobSubmitterFactory.factory( job.type, str(job.id), @@ -342,12 +339,12 @@ def check_job_status(job): _fail(job, error_message) command_processor.delay(Command(CommandType.CHECK_COMMAND_LINE_STATUS, str(job.id)).to_dict()) - elif lsf_status in (Status.FAILED,): - _fail(job, lsf_message) + elif batch_system_status in (Status.FAILED,): + _fail(job, batch_system_message) command_processor.delay(Command(CommandType.CHECK_COMMAND_LINE_STATUS, str(job.id)).to_dict()) else: - raise StopException("Invalid transition %s to %s" % (Status(job.status).name, Status(lsf_status).name)) + raise StopException("Invalid transition %s to %s" % (Status(job.status).name, Status(batch_system_status).name)) def _add_alert(job, alert_obj): @@ -465,8 +462,7 @@ def terminate_job(job): Status.SUSPENDED, Status.UNKNOWN, ): - lsf_client = LSFClient() - job_killed = lsf_client.terminate(str(job.id)) + job_killed = BATCH_SYSTEM.terminate(str(job.id)) if not job_killed: raise RetryException("Failed to TERMINATE job %s" % str(job.id)) job.terminate() @@ -523,17 +519,17 @@ def full_cleanup_jobs(self): @shared_task(bind=True) def cleanup_completed_jobs(self): - cleanup_jobs(Status.COMPLETED, settings.CLEANUP_COMPLETED_JOBS, exclude=["input.json", "lsf.log"]) + cleanup_jobs(Status.COMPLETED, settings.CLEANUP_COMPLETED_JOBS, exclude=["input.json", BATCH_SYSTEM.logfileName]) @shared_task(bind=True) def cleanup_failed_jobs(self): - cleanup_jobs(Status.FAILED, settings.CLEANUP_FAILED_JOBS, exclude=["input.json", "lsf.log"]) + cleanup_jobs(Status.FAILED, settings.CLEANUP_FAILED_JOBS, exclude=["input.json", BATCH_SYSTEM.logfileName]) @shared_task(bind=True) def cleanup_terminated_jobs(self): - cleanup_jobs(Status.TERMINATED, settings.CLEANUP_TERMINATED_JOBS, exclude=["input.json", "lsf.log"]) + cleanup_jobs(Status.TERMINATED, settings.CLEANUP_TERMINATED_JOBS, exclude=["input.json", BATCH_SYSTEM.logfileName]) def cleanup_jobs(status, time_delta, exclude=[]): From 1a643cc77c20b10a1a87a8f59ac20e1bc9e93e54 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 12:40:13 -0500 Subject: [PATCH 005/235] Update job submitter to use abstract batch system --- .../nextflow_jobsubmitter.py | 8 +-- submitter/toil_submitter/toil_jobsubmitter.py | 53 ++++++++++--------- submitter/toil_submitter/toil_track_utils.py | 1 + 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index b1ac97fc..d5e7937c 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -4,6 +4,7 @@ import json from django.conf import settings from submitter import JobSubmitter +from batch_systems.batch_system import get_batch_system class NextflowJobSubmitter(JobSubmitter): @@ -61,6 +62,7 @@ def __init__( self.job_work_dir = os.path.join(dir_config["WORK_DIR_ROOT"], self.job_id) self.job_outputs_dir = root_dir self.job_tmp_dir = os.path.join(dir_config["TMP_DIR_ROOT"], self.job_id) + self.batch_system = get_batch_system() def prepare_to_submit(self): self._prepare_directories() @@ -69,7 +71,7 @@ def prepare_to_submit(self): def get_submit_command(self): command_line = self._command_line() - log_path = os.path.join(self.job_work_dir, "lsf.log") + log_path = os.path.join(self.job_work_dir, self.batch_system.logfileName) env = dict() env["NXF_OPTS"] = settings.NEXTFLOW_NXF_OPTS env["JAVA_HOME"] = settings.NEXTFLOW_JAVA_HOME @@ -84,10 +86,10 @@ def _leader_args(self): return args def _walltime(self): - return ["-W", str(self.walltime)] if self.walltime else [] + return self.batch_system.set_walltime(self.walltime) def _memlimit(self): - return ["-M", self.memlimit] if self.memlimit else ["-M", "20"] + return self.batch_system.set_memlimit(self.memlimit, default="20") def _sha1(self, path, buffersize=1024 * 1024): try: diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index a1da66ae..83a9c355 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -7,7 +7,7 @@ from orchestrator.models import Status from submitter import JobSubmitter from .toil_track_utils import ToilTrack, ToolStatus -from batch_systems.lsf_client.lsf_client import format_lsf_job_id +from batch_systems.batch_system import get_batch_system def translate_toil_to_model_status(status): @@ -50,6 +50,12 @@ def __init__( self.job_work_dir = os.path.join(dir_config["WORK_DIR_ROOT"], self.job_id) self.job_outputs_dir = root_dir self.job_tmp_dir = os.path.join(dir_config["TMP_DIR_ROOT"], self.job_id) + self.batch_system = get_batch_system() + self.batch_system_args_env = None + if settings.BATCH_SYSTEM == "LSF": + self.batch_system_args_env = "TOIL_LSF_ARGS" + elif settings.BATCH_STYSTEM == "SLURM": + self.batch_system_args_env = "TOIL_SLURM_ARGS" def prepare_to_submit(self): self._prepare_directories() @@ -59,18 +65,15 @@ def prepare_to_submit(self): def get_submit_command(self): command_line = self._command_line() - log_path = os.path.join(self.job_work_dir, "lsf.log") + log_path = os.path.join(self.job_work_dir, self.batch_system.logfileName) env = dict() - if settings.LSF_SLA: - toil_lsf_args = "-sla %s %s %s" % ( - settings.LSF_SLA, - " ".join(self._job_group()), - " ".join(self._tool_args()), - ) - else: - toil_lsf_args = "%s %s" % (" ".join(self._job_group()), " ".join(self._tool_args())) + toil_batch_system_args = "%s %s %s" % ( + " ".join(self._service_queue()), + " ".join(self._job_group()), + " ".join(self._tool_args()), + ) env["JAVA_HOME"] = None - env["TOIL_LSF_ARGS"] = toil_lsf_args + env[self.batch_system_args_env] = toil_batch_system_args.strip() return command_line, self._leader_args(), log_path, self.job_id, env def get_commandline_status(self, cache): @@ -125,18 +128,18 @@ def get_commandline_status(self, cache): def get_outputs(self): error_message = None result_json = None - lsf_log_path = os.path.join(self.job_work_dir, "lsf.log") + log_path = os.path.join(self.job_work_dir, self.batch_system.logfileName) try: - with open(lsf_log_path, "r") as f: + with open(log_path, "r") as f: data = f.readlines() data = "".join(data) substring = data.split("\n{")[1] result = ("{" + substring).split("-----------")[0] result_json = json.loads(result) except (IndexError, ValueError): - error_message = "Could not parse json from %s" % lsf_log_path + error_message = "Could not parse json from %s" % log_path except FileNotFoundError: - error_message = "Could not find %s" % lsf_log_path + error_message = "Could not find %s" % log_path if self.log_dir: output_log_location = os.path.join(self.log_dir, "output.json") @@ -190,18 +193,21 @@ def _tool_args(self): if self.tool_walltime: expected_limit = max(1, int(self.tool_walltime / 3)) hard_limit = self.tool_walltime - args = ["-We", str(expected_limit), "-W", str(hard_limit)] + args = self.batch_system.set_walltime(expected_limit, hard_limit) args.extend(self._memlimit()) return args + def _service_queue(self): + return self.batch_system.set_service_queue() + def _walltime(self): - return ["-W", str(self.walltime)] if self.walltime else [] + return self.batch_system.set_walltime(None, self.walltime) def _memlimit(self): - return ["-M", self.memlimit] if self.memlimit else [] + return self.batch_system.set_memlimit(self.memlimit) def _job_group(self): - return ["-g", format_lsf_job_id(self.job_id)] + return self.batch_system.set_group(self.job_id) def _command_line(self): single_machine_mode_workflows = ["nucleo_qc", "argos-qc"] @@ -219,7 +225,7 @@ def _command_line(self): "--logFile", "toil_log.log", "--batchSystem", - "lsf", + self.batch_system.name, "--logLevel", "DEBUG", "--stats", @@ -234,7 +240,7 @@ def _command_line(self): "--preserve-environment", "PATH", "TMPDIR", - "TOIL_LSF_ARGS", + self.batch_system_args_env, "CWL_SINGULARITY_CACHE", "PWD", "_JAVA_OPTIONS", @@ -275,7 +281,6 @@ def _command_line(self): "--preserve-environment", "PATH", "TMPDIR", - "TOIL_LSF_ARGS", "CWL_SINGULARITY_CACHE", "SINGULARITYENV_LC_ALL", "PWD", @@ -309,7 +314,7 @@ def _command_line(self): "--logFile", "toil_log.log", "--batchSystem", - "lsf", + self.batch_system.name, "--statePollingWait", str(settings.TOIL_STATE_POLLING_WAIT), "--disable-user-provenance", @@ -324,7 +329,7 @@ def _command_line(self): "--preserve-environment", "PATH", "TMPDIR", - "TOIL_LSF_ARGS", + self.batch_system_args_env, "CWL_SINGULARITY_CACHE", "SINGULARITYENV_LC_ALL", "PWD", diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index a1220406..12c5fcc9 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -1,6 +1,7 @@ """ Track commandline status of toil jobs """ + import os import sys import logging From 26e2d4f085c4262a995e6d7216b50c5a1e05f690 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 13:34:12 -0500 Subject: [PATCH 006/235] Add SLURM arguments and made LSF env optional --- container/Readme.md | 7 +++++++ container/celery_services.def | 31 ++++++++++++++----------------- container/ridgeback_service.def | 17 +++++++++-------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/container/Readme.md b/container/Readme.md index beabc523..d05ea173 100644 --- a/container/Readme.md +++ b/container/Readme.md @@ -68,6 +68,13 @@ The default rabbitmq queue should be the same queue set in orchestrator/celery.p | :-------------------------------------- | :----------------- | :------ | | SINGULARITYENV_RIDGEBACK_LSF_STACKLIMIT | stacklimit for LSF | None | +##### SLURM + +| Variable | Description | +| :--------------------------------------- | :--------------------------- | +| SINGULARITYENV_SLURM_BINDIR | The path to the lsf bin dir | +| SINGULARITYENV_RIDGEBACK_SLURM_PARTITION | Partition for the SLURM jobs | + ##### Celery | Optional Variable | Description | Default | diff --git a/container/celery_services.def b/container/celery_services.def index c5983b46..75577914 100644 --- a/container/celery_services.def +++ b/container/celery_services.def @@ -105,53 +105,50 @@ Includecmd: no echo "ERROR: SINGULARITYENV_RIDGEBACK_CELERY_EVENT_QUEUE_PREFIX not set." exit 1 fi + + ### SLURM env variables + if [ -z "$SLURM_BINDIR" ]; then + echo "WARNING environment variable SLURM_BINDIR is not defined, SLURM may not work" + fi ### LSF env variables if [ -z "$LSF_LIBDIR" ]; then - echo "ERROR environment variable LSF_LIBDIR is not defined, LSF will not work" - exit 1 + echo "WARNING environment variable LSF_LIBDIR is not defined, LSF will not work" fi if [ -z "$LSF_SERVERDIR" ]; then - echo "ERROR environment variable LSF_SERVERDIR is not defined, LSF will not work" - exit 1 + echo "WARNING environment variable LSF_SERVERDIR is not defined, LSF will not work" fi if [ -z "$LSF_ENVDIR" ]; then - echo "ERROR environment variable LSF_ENVDIR is not defined, LSF will not work" - exit 1 + echo "WARNING environment variable LSF_ENVDIR is not defined, LSF will not work" fi if [ -z "$LSF_BINDIR" ]; then - echo "ERROR environment variable LSF_BINDIR is not defined, LSF will not work" - exit 1 + echo "WARNING environment variable LSF_BINDIR is not defined, LSF will not work" fi if [ ! -d "$LSF_LIBDIR" ]; then - echo "ERROR $LSF_LIBDIR is not mounted or does not exist" - exit 1 + echo "WARNING $LSF_LIBDIR is not mounted or does not exist" fi if [ ! -d "$LSF_SERVERDIR" ]; then - echo "ERROR $LSF_SERVERDIR is not mounted or does not exist" - exit 1 + echo "WARNING $LSF_SERVERDIR is not mounted or does not exist" fi if [ ! -d "$LSF_ENVDIR" ]; then - echo "ERROR $LSF_ENVDIR is not mounted or does not exist" - exit 1 + echo "WARNING $LSF_ENVDIR is not mounted or does not exist" fi if [ ! -d "$LSF_BINDIR" ]; then - echo "ERROR $LSF_BINDIR is not mounted or does not exist" - exit 1 + echo "WARNING $LSF_BINDIR is not mounted or does not exist" fi RIDGEBACK_VENV_ACTIVATE=$RIDGEBACK_VENV/bin/activate echo "Activating venv $RIGEBACK_VENV" . $RIDGEBACK_VENV_ACTIVATE - export PATH=$LSF_BINDIR:$SINGULARITY_BIN_PATH:$PATH + export PATH=$SLURM_BINDIR:$LSF_BINDIR:$SINGULARITY_BIN_PATH:$PATH echo "Running orchestrator beat..." nohup celery --workdir ${RIDGEBACK_PATH} \ diff --git a/container/ridgeback_service.def b/container/ridgeback_service.def index 154c83a2..30f046f8 100644 --- a/container/ridgeback_service.def +++ b/container/ridgeback_service.def @@ -64,25 +64,26 @@ Includecmd: no echo "RIDGEBACK_TOIL set to $RIDGEBACK_TOIL" >> $RIDGEBACK_HOME/logs/boot.log fi + ### SLURM env variables + if [ -z "$SLURM_BINDIR" ]; then + echo "WARNING environment variable SLURM_BINDIR is not defined, SLURM may not work" + fi + ### LSF Parameters to communicate with LSF if [ -z "$LSF_ENVDIR" ]; then - echo "ERROR: SINGULARITYENV_LSF_ENVDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log - exit 1 + echo "WARNING: SINGULARITYENV_LSF_ENVDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log fi if [ -z "$LSF_BINDIR" ]; then - echo "ERROR: SINGULARITYENV_LSF_BINDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log - exit 1 + echo "WARNING: SINGULARITYENV_LSF_BINDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log fi if [ -z "$LSF_LIBDIR" ]; then - echo "ERROR: SINGULARITYENV_LSF_LIBDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log - exit 1 + echo "WARNING: SINGULARITYENV_LSF_LIBDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log fi if [ -z "$LSF_SERVERDIR" ]; then - echo "ERROR: SINGULARITYENV_LSF_SERVERDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log - exit 1 + echo "WARNING: SINGULARITYENV_LSF_SERVERDIR is not set." >> $RIDGEBACK_HOME/logs/boot.log fi python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput From 58663fd8c606e2ca28d494aa62ae9052081691d2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 13:34:42 -0500 Subject: [PATCH 007/235] Add batch system and SLURM setting --- ridgeback/settings.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 82fbe5b5..bb9cdcee 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -281,11 +281,21 @@ "TMP_DIR_ROOT": os.environ["DEFAULT_TMP_DIR_ROOT"], }, } +# Batch System settings -# Toil settings +BATCH_SYSTEM = os.environ.get("RIDGEBACK_BATCH_SYSTEM", "LSF") + +# SLURM settings + +SLURM_PARTITION = os.environ.get("RIDGEBACK_SLURM_PARTITION", None) + +# LSF settings LSF_WALLTIME = os.environ["RIDGEBACK_LSF_WALLTIME"] LSF_SLA = os.environ.get("RIDGEBACK_LSF_SLA", None) + +# Toil settings + CWLTOIL = os.environ.get("RIDGEBACK_TOIL", "toil-cwl-runner") TOIL_STATE_POLLING_WAIT = os.environ.get("TOIL_STATE_POLLING_WAIT", 60) TOIL_MAX_CORES = os.environ.get("RIDGEBACK_TOIL_MAX_CORES", "24") From e7b6a788d964c438fab51c37bc7fab275ba98b8e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 13:35:37 -0500 Subject: [PATCH 008/235] Add SLURM client --- batch_systems/slurm_client/__init__.py | 3 + batch_systems/slurm_client/slurm_client.py | 317 +++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 batch_systems/slurm_client/__init__.py create mode 100644 batch_systems/slurm_client/slurm_client.py diff --git a/batch_systems/slurm_client/__init__.py b/batch_systems/slurm_client/__init__.py new file mode 100644 index 00000000..2926f010 --- /dev/null +++ b/batch_systems/slurm_client/__init__.py @@ -0,0 +1,3 @@ +from .slurm_client import SLURMClient + +__all__ = ["SLURMClient"] diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py new file mode 100644 index 00000000..53d3520e --- /dev/null +++ b/batch_systems/slurm_client/slurm_client.py @@ -0,0 +1,317 @@ +""" +Submit, monitor, and control SLURM jobs +""" + +import os +import re +import subprocess +import json +import logging +from django.conf import settings +from orchestrator.models import Status +from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException +from batch_systems.batch_system import BatchClient + + +class SLURMClient(BatchClient): + """ + Client for SLURM + + Attributes: + logger (logging): logging module + """ + + def __init__(self): + """ + init function + """ + self.logger = logging.getLogger("SLURM_client") + self.logfileName = "slurm.log" + self.name = "slurm" + + def submit(self, command, job_args, stdout, job_id, env={}): + """ + Submit command to SLURM and store log in stdout + + Args: + command (str): command to submit + job_args (list): Additional options for leader sbatch + stdout (str): log file path + job_id (str): job_id + env (dict): Environment variables + + Returns: + int: slurm job id + """ + + sbatch_command = ( + ["sbatch"] + + self.set_service_queue() + + self.set_group(job_id) + + self.set_stdout_file(stdout) + + job_args + + [f"--wrap=exec {command}"] + ) + current_env = os.environ.copy() + for k, v in env.items(): + if v: + current_env[k] = v + elif k in current_env: + current_env.pop(k) + self.logger.debug("Running command: %s\nEnv: %s", sbatch_command, current_env) + process = subprocess.run( + sbatch_command, + check=True, + stdout=subprocess.PIPE, + universal_newlines=True, + env=current_env, + ) + if process.returncode != 0: + self.logger.exception(f"Failed to submit job to SLURM. Process return_code: {process.returncode}") + raise FailToSubmitToSchedulerException( + f"Failed to submit job to SLURM. Process return_code: {process.returncode}" + ) + return self._parse_procid(process.stdout) + + def terminate(self, job_id): + """ + Kill SLURM job + + Args: + job_id (str): job_id + + Returns: + bool: successful + """ + self.logger.debug("Terminating SLURM jobs for job %s", job_id) + scancel_command = ["scancel", f"--wckey={job_id}"] + process = subprocess.run(scancel_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) + if process.returncode in (0, 255): + return True + return False + + def set_walltime(self, expected_limit, hard_limit): + walltime_args = [] + if expected_limit: + walltime_args = walltime_args + [f"--t={expected_limit}"] + if hard_limit: + self.logger.debug( + "Hard limits are no supported on submission, its configured by the clusters's KillWait and OverTimeLimit parameters" + ) + return walltime_args + + def set_memlimit(self, mem_limit, default=None): + mem_limit_args = [] + if default: + mem_limit = [f"--mem={default}"] + if mem_limit: + mem_limit_args = [f"--mem={mem_limit}"] + return mem_limit_args + + def set_group(self, group_id): + group_id_args = [] + if group_id: + group_id_args = [f"--wckey={group_id}"] + return group_id_args + + def set_stdout_file(self, stdout_file): + if stdout_file: + return [f"--output={stdout_file}"] + return [f"--output={self.logfileName}"] + + def set_service_queue(self): + service_queue_args = [] + if settings.SLURM_PARTITION: + service_queue_args = [f"--partition={settings.SLURM_PARTITION}"] + return service_queue_args + + def _parse_sacct(self, sacct_output_str, external_job_id): + """ + Parse the output of sacct into a descriptive dict + + Args: + sacct_output_str (str): Stdout from sacct + external_job_id (str): SLURM job id + + Returns: + tuple: sacct job record (id,status,batch_system_exitcode,tool_exitcode) + """ + sacct_record = None + if not sacct_output_str: + self.logger.error(f"Error - sacct command could not find job {external_job_id}") + else: + slurm_job_info = sacct_output_str.split("|") + slurm_id = slurm_job_info[0].split(".")[0] + status = slurm_job_info[1] + exitcode_batch = slurm_job_info[2].split(":")[0] + exitcode_tool = slurm_job_info[2].split(":")[1] + sacct_record = (slurm_id, status, exitcode_batch, exitcode_tool) + return sacct_record + + def _parse_procid(self, stdout): + """ + Parse sbatch output and retrieve the SLURM id + + Args: + stdout (str): sbatch output + + Returns: + int: SLURM id + """ + self.logger.debug("SLURM returned %s", stdout) + slurm_job_id_search = re.search("Submitted batch job (.*)", stdout) + if slurm_job_id_search: + slurm_job_id = int(slurm_job_id_search[1]) + self.logger.debug("Got the job id: %s", slurm_job_id) + return slurm_job_id + else: + self.logger.error("Could not parse job_id. Job is not submitted to SLURM\nReason: %s", stdout) + raise FailToSubmitToSchedulerException(f"Reason: {stdout}") + + def _handle_status(self, process_status, batchsystem_exitcode, tool_exitcode, external_job_id): + """ + Map SLURM status to Ridgeback status + + Args: + process_status (str): SLURM status of process + batchsystem_exitcode (str): Exitcode from the batchsystem + tool_exitcode (str): Exitcode form the tool + + Returns: + tuple: (Ridgeback Status int, extra info) + """ + + # If a job is in one of these states, it might eventually move to a different + # state. + + if process_status == "COMPLETED": + self.logger.debug("Job [%s] completed", external_job_id) + return Status.COMPLETED, None + if process_status in [ + "PENDING", + "CONFIGURING", + "REQUEUED", + "REQUEUE_FED", + "REQUEUE_HOLD", + "RESIZING", + "RESV_DEL_HOLD", + "POWER_UP_NODE", + ]: + self.logger.debug("Job [%s] is pending", external_job_id) + return Status.PENDING, None + if process_status in [ + "BOOT_FAIL", + "LAUNCH_FAILED", + "CANCELLED", + "DEADLINE", + "FAILED", + "NODE_FAIL", + "OUT_OF_MEMORY", + "PREEMPTED", + "REVOKED", + "SPECIAL_EXIT", + "RECONFIG_FAIL", + "TIMEOUT", + ]: + exit_info = ( + f"{process_status}, tool exit code: {tool_exitcode}, batchsystem exit code: {batchsystem_exitcode}" + ) + self.logger.error("Job [%s] failed with: %s", external_job_id, exit_info) + return Status.FAILED, exit_info.strip() + if process_status in ["RUNNING", "COMPLETING", "STAGE_OUT"]: + self.logger.debug("Job [%s] is running", external_job_id) + return Status.RUNNING, None + if process_status in ["SUSPENDED", "STOPPED"]: + self.logger.debug("Job [%s] is suspended", external_job_id) + suspended_info = "Job suspended" + return Status.SUSPENDED, suspended_info.strip() + self.logger.debug("Job [%s] is in an unhandled state (%s)", external_job_id, process_status) + status_info = "Job is in an unhandles state: {}".format(process_status) + return Status.UNKNOWN, status_info.strip() + + def _parse_status(self, stdout, external_job_id): + """Parse SLURM stdout helper + + Args: + stdout (str): stdout of bjobs + external_job_id (str): SLURM id + + Returns: + tuple: (Ridgeback Status int, extra info) + """ + + sacct_record = self._parse_sacct(stdout) + if sacct_record: + status = sacct_record[1] + exit_batch = sacct_record[2] + exit_tool = sacct_record[3] + return self._handle_status(status, exit_batch, exit_tool, external_job_id) + + raise FetchStatusException(f"Failed to get status for job {external_job_id}") + + def status(self, external_job_id): + """Parse SLURM status + + Args: + external_job_id (str): SLURM id + + Returns: + tuple: (Ridgeback Status int, extra info) + """ + saact_command = ["sacct", f"--jobs={external_job_id}.batch", "--format='jobid,state,exitcode'", "-n", "-P"] + self.logger.debug("Checking slurm status for job: %s", external_job_id) + process = subprocess.run(saact_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) + status = self._parse_status(process.stdout, external_job_id) + return status + + def _get_job_list(self, job_id): + """Get slurm job ids in a group + + Args: + job_id (str): id of job + + Returns: + list: SLURM job ids + """ + + slurm_jobs = [] + saact_command = ["sacct", f"--wckeys={job_id}", "--format='jobid'", "-n", "-P"] + process = subprocess.run(saact_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) + output = process.stdout + for single_slurm_id in output: + slurm_jobs.append(single_slurm_id) + return slurm_jobs + + def suspend(self, job_id): + """ + Suspend SLURM job + Args: + job_id (str): id of job + Returns: + bool: successful + """ + self.logger.debug("Suspending SLURM jobs for job %s", job_id) + job_list = self._get_job_list(job_id) + job_list_str = ",".join(job_list) + scontrol_command = ["scontrol", "suspend", f"{job_list_str}"] + process = subprocess.run(scontrol_command, stdout=subprocess.PIPE, universal_newlines=True) + if process.returncode == 0: + return True + return False + + def resume(self, job_id): + """ + Resume SLURM job + Args: + job_id (str): id of job + Returns: + bool: successful + """ + self.logger.debug("Resuming SLURM jobs for job %s", job_id) + job_list = self._get_job_list(job_id) + job_list_str = ",".join(job_list) + scontrol_command = ["scontrol", "resume", f"{job_list_str}"] + process = subprocess.run(scontrol_command, stdout=subprocess.PIPE, universal_newlines=True) + if process.returncode == 0: + return True + return False From 7ff1008251980c59bf456d4c6cf3d8e804ed536d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 13:39:19 -0500 Subject: [PATCH 009/235] Add black formatting --- ridgeback/urls.py | 1 + tests/test_commandline.py | 1 + 2 files changed, 2 insertions(+) diff --git a/ridgeback/urls.py b/ridgeback/urls.py index 777f9907..3d7df427 100644 --- a/ridgeback/urls.py +++ b/ridgeback/urls.py @@ -13,6 +13,7 @@ 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ + from django.contrib import admin from django.conf.urls import url from django.urls import path, include diff --git a/tests/test_commandline.py b/tests/test_commandline.py index 686d7ecf..335ef142 100644 --- a/tests/test_commandline.py +++ b/tests/test_commandline.py @@ -1,6 +1,7 @@ """ Tests for commandline status handling """ + import os from shutil import unpack_archive, copytree, copy import tempfile From 20dc84b58fa831fd1abe313b2a09168691e71bec Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 13:40:19 -0500 Subject: [PATCH 010/235] Update lsf client tests --- tests/test_lsf_client.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_lsf_client.py b/tests/test_lsf_client.py index 2cf59985..12e24132 100644 --- a/tests/test_lsf_client.py +++ b/tests/test_lsf_client.py @@ -103,6 +103,32 @@ def test_terminate(self, terminate_process): self.assertEqual(terminate_process.call_args[0][0], expected_command) self.assertEqual(terminated, True) + @patch("subprocess.run") + def test_suspend(self, suspend_process): + """ + Test LSF suspend + """ + suspend_process_obj = Mock() + suspend_process_obj.returncode = 0 + suspend_process.return_value = suspend_process_obj + expected_command = ["bstop", "-g", self.example_lsf_id, "0"] + suspended = self.lsf_client.suspend(self.example_job_id) + self.assertEqual(suspend_process.call_args[0][0], expected_command) + self.assertEqual(suspended, True) + + @patch("subprocess.run") + def test_resume(self, resume_process): + """ + Test LSF resume + """ + resume_process_obj = Mock() + resume_process_obj.returncode = 0 + resume_process.return_value = resume_process_obj + expected_command = ["bresume", "-g", self.example_lsf_id, "0"] + resumed = self.lsf_client.resume(self.example_job_id) + self.assertEqual(resume_process.call_args[0][0], expected_command) + self.assertEqual(resumed, True) + @patch("subprocess.run") def test_failed_status(self, status_process): """ From fc9a49e97d0225f51756f78afad66fbe162465ed Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 13:40:32 -0500 Subject: [PATCH 011/235] Added SLURM tests --- tests/test_slurm_client.py | 158 +++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 tests/test_slurm_client.py diff --git a/tests/test_slurm_client.py b/tests/test_slurm_client.py new file mode 100644 index 00000000..f756374d --- /dev/null +++ b/tests/test_slurm_client.py @@ -0,0 +1,158 @@ +from django.test import TestCase +from mock import patch, Mock +from django.conf import settings +from batch_systems.slurm_client.slurm_client import SLURMClient +from orchestrator.models import Status + + +class TestSLURMClient(TestCase): + """ + Test LSF Client + """ + + def setUp(self): + self.example_id = 12345678 + self.example_job_id = "d736e17e-d67a-4897-901b-decab9942398" + self.submit_response = "Submitted batch job {}".format(self.example_id) + self.slurm_client = SLURMClient() + self.example_partion = "partition1" + self.status_completed_response = """ + {}|COMPLETED|0:0 + {}.batch|COMPLETED|0:0 + {}.extern|COMPLETED|0:0 + """.format( + self.example_id, self.example_id, self.example_id + ) + self.status_failed_response = """ + {}|FAILED|1:0 + {}.batch|FAILED|1:0 + {}.extern|COMPLETED|0:0 + """.format( + self.example_id, self.example_id, self.example_id + ) + self.status_pend_response = """ + {}|PENDING|0:0 + """.format( + self.example_id + ) + self.exit_reason = "FAILED, tool exit code: 1, batchsystem exit code: 0" + self.pend_reason = None + + @patch("subprocess.run") + def test_submit(self, submit_process): + """ + Test SLURM submit + """ + command = "ls" + args = [] + stdout_file = "stdout.txt" + submit_process_obj = Mock() + submit_process_obj.stdout = self.submit_response + submit_process_obj.returncode = 0 + submit_process.return_value = submit_process_obj + slurm_id = self.slurm_client.submit(command, args, stdout_file, self.example_job_id, {}) + with self.settings(SLURM_PARTITION=self.example_partion): + expected_command = ( + [ + "sbatch", + f"--partition={self.example_partion}", + f"--wckey={self.example_job_id}", + f"--output={self.slurm_client.logfileName}", + ] + + args + + [f"--wrap=exec {command}"] + ) + self.assertEqual(slurm_id, self.example_id) + self.assertEqual(submit_process.call_args[0][0], expected_command) + + @patch("subprocess.run") + def test_submit_with_args(self, submit_process): + """ + Test SLURM submit with mem and walltime args + """ + command = "ls" + args = [] + stdout_file = "stdout.txt" + submit_process_obj = Mock() + submit_process_obj.stdout = self.submit_response + submit_process_obj.returncode = 0 + submit_process.return_value = submit_process_obj + expected_limit = 10 + mem_limit = 8 + args = self.slurm_client.set_walltime(expected_limit, None) + args.extend(self.slurm_client.set_memlimit(mem_limit)) + with self.settings(SLURM_PARTITION=self.example_partion): + slurm_id = self.slurm_client.submit(command, args, stdout_file, self.example_job_id, {}) + expected_command = ( + [ + "sbatch", + f"--partition={self.example_partion}", + f"--wckey={self.example_job_id}", + f"--output={self.slurm_client.logfileName}", + ] + + args + + [f"--wrap=exec {command}"] + ) + self.assertEqual(slurm_id, self.example_id) + self.assertEqual(submit_process.call_args[0][0], expected_command) + + @patch("subprocess.run") + def test_terminate(self, terminate_process): + """ + Test SLURM terminate + """ + terminate_process_obj = Mock() + terminate_process_obj.returncode = 0 + terminate_process.return_value = terminate_process_obj + expected_command = ["scancel", f"--wckey={self.example_job_id}"] + terminated = self.slurm_client.terminate(self.example_job_id) + self.assertEqual(terminate_process.call_args[0][0], expected_command) + self.assertEqual(terminated, True) + + @patch("subprocess.run") + def test_suspend(self, suspend_process): + """ + Test SLURM suspend + """ + suspend_process.side_effect = [f"{self.example_id}", ""] + expected_command = ["scontrol", "suspend", f"{self.example_id}"] + suspended = self.slurm_client.suspend(self.example_job_id) + self.assertEqual(suspend_process.call_args[0][0], expected_command) + self.assertEqual(suspended, True) + + @patch("subprocess.run") + def test_resume(self, resume_process): + """ + Test SLURM resume + """ + resume_process.side_effect = [f"{self.example_job_id}", ""] + expected_command = ["scontrol", "resume", f"{self.example_id}"] + resumed = self.slurm_client.resume(self.example_job_id) + self.assertEqual(resume_process.call_args[0][0], expected_command) + self.assertEqual(resumed, True) + + @patch("subprocess.run") + def test_failed_status(self, status_process): + """ + Test SLURM failed status + """ + status_process_obj = Mock() + status_process_obj.returncode = 0 + status_process_obj.stdout = self.status_failed_response + status_process.return_value = status_process_obj + status = self.slurm_client.status(self.example_id) + expected_status = Status.FAILED, self.exit_reason + self.assertEqual(status, expected_status) + + @patch("subprocess.run") + def test_pend_status(self, status_process): + """ + Test SLURM pending status + """ + status_process_obj = Mock() + status_process_obj.returncode = 0 + status_process_obj.stdout = self.status_pend_response + status_process.return_value = status_process_obj + status = self.slurm_client.status(self.example_id) + expected_status = Status.PENDING, self.pend_reason + self.assertEqual(status, expected_status) From ea4eb2a0c33b84e7a4207c8594494f55e171dde8 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 16:50:14 -0500 Subject: [PATCH 012/235] Fixed batch system import --- batch_systems/batch_system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index e2b5fd45..2324388a 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -1,13 +1,13 @@ from django.conf import settings import logging -from batch_systems.lsf_client.lsf_client import LSFClient -from batch_systems.slurm_client.slurm_client import SLURMClient def get_batch_system(): if settings.BATCH_SYSTEM == "LSF": + from batch_systems.lsf_client.lsf_client import LSFClient return LSFClient() elif settings.BATCH_SYSTEM == "SLURM": + from batch_systems.slurm_client.slurm_client import SLURMClient return SLURMClient() else: raise Exception(f"Batch system {settings.BATCH_SYSTEM} not supported, please use either LSF or SLURM") From 807d46167e39c6e25f7b9c648f8fa7cb01674cad Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 16:50:50 -0500 Subject: [PATCH 013/235] Fixed sacctl parsing --- batch_systems/slurm_client/slurm_client.py | 29 +++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 53d3520e..dbb9c2ad 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -137,15 +137,18 @@ def _parse_sacct(self, sacct_output_str, external_job_id): tuple: sacct job record (id,status,batch_system_exitcode,tool_exitcode) """ sacct_record = None - if not sacct_output_str: + if sacct_output_str: + output_lines = sacct_output_str.strip().split("\n") + for single_sacct_line in output_lines: + slurm_job_info = single_sacct_line.strip().split("|") + slurm_id = slurm_job_info[0] + if slurm_id == external_job_id: + status = slurm_job_info[1] + exitcode_batch = slurm_job_info[2].split(":")[0] + exitcode_tool = slurm_job_info[2].split(":")[1] + sacct_record = (slurm_id, status, exitcode_batch, exitcode_tool) + if not sacct_record: self.logger.error(f"Error - sacct command could not find job {external_job_id}") - else: - slurm_job_info = sacct_output_str.split("|") - slurm_id = slurm_job_info[0].split(".")[0] - status = slurm_job_info[1] - exitcode_batch = slurm_job_info[2].split(":")[0] - exitcode_tool = slurm_job_info[2].split(":")[1] - sacct_record = (slurm_id, status, exitcode_batch, exitcode_tool) return sacct_record def _parse_procid(self, stdout): @@ -240,7 +243,7 @@ def _parse_status(self, stdout, external_job_id): tuple: (Ridgeback Status int, extra info) """ - sacct_record = self._parse_sacct(stdout) + sacct_record = self._parse_sacct(stdout, external_job_id) if sacct_record: status = sacct_record[1] exit_batch = sacct_record[2] @@ -261,7 +264,7 @@ def status(self, external_job_id): saact_command = ["sacct", f"--jobs={external_job_id}.batch", "--format='jobid,state,exitcode'", "-n", "-P"] self.logger.debug("Checking slurm status for job: %s", external_job_id) process = subprocess.run(saact_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) - status = self._parse_status(process.stdout, external_job_id) + status = self._parse_status(process.stdout, str(external_job_id)) return status def _get_job_list(self, job_id): @@ -278,8 +281,10 @@ def _get_job_list(self, job_id): saact_command = ["sacct", f"--wckeys={job_id}", "--format='jobid'", "-n", "-P"] process = subprocess.run(saact_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) output = process.stdout - for single_slurm_id in output: - slurm_jobs.append(single_slurm_id) + for single_line in output.strip().split("\n"): + single_slurm_id = single_line.split(".")[0] + if single_slurm_id and single_slurm_id not in slurm_jobs: + slurm_jobs.append(single_slurm_id) return slurm_jobs def suspend(self, job_id): From 587eae38bdf62e4de00a52d6490bec3182652174 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 16:51:48 -0500 Subject: [PATCH 014/235] Removed lsf references --- orchestrator/tests/test_tasks.py | 4 ++-- tests/test_api.py | 4 ++-- tests/test_tasks.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/orchestrator/tests/test_tasks.py b/orchestrator/tests/test_tasks.py index 4a91ef2c..acc6b4f2 100644 --- a/orchestrator/tests/test_tasks.py +++ b/orchestrator/tests/test_tasks.py @@ -410,7 +410,7 @@ def test_process_jobs(self, check_leader_not_running, command_processor, add, de process_jobs() calls = [ - call(Command(CommandType.CHECK_STATUS_ON_LSF, str(job_pending_1.id)).to_dict()), + call(Command(CommandType.CHECK_STATUS_ON_BATCH_SYSTEM, str(job_pending_1.id)).to_dict()), call(Command(CommandType.PREPARE, str(job_created_1.id)).to_dict()), ] @@ -441,7 +441,7 @@ def _raise_retryable_exception(job_id): delete.return_value = True status.side_effect = _raise_retryable_exception with self.assertRaises(RetryException): - command_processor(Command(CommandType.CHECK_STATUS_ON_LSF, str(job_pending_1.id)).to_dict()) + command_processor(Command(CommandType.CHECK_STATUS_ON_BATCH_SYSTEM, str(job_pending_1.id)).to_dict()) @patch("django.core.cache.cache.delete") @patch("django.core.cache.cache.add") diff --git a/tests/test_api.py b/tests/test_api.py index 8e0b7092..0537f571 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -45,7 +45,7 @@ def test_404_read(self): response = self.client.get(url) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - @patch("orchestrator.tasks.submit_job_to_lsf") + @patch("orchestrator.tasks.submit_job_to_batch_system") def test_create(self, submit_jobs_mock): ddtrace.tracer.enabled = False url = self.api_root + "jobs/" @@ -80,7 +80,7 @@ def test_delete_authorized(self): response = self.client.delete(url) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - @patch("orchestrator.tasks.submit_job_to_lsf") + @patch("orchestrator.tasks.submit_job_to_batch_system") def test_resume(self, submit_jobs_mock): ddtrace.tracer.enabled = False url = "{}jobs/{}/resume/".format(self.api_root, self.example_job.id) diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 54da768a..03e56a1b 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -3,7 +3,7 @@ from orchestrator.models import Job, Status, PipelineType from orchestrator.tasks import ( prepare_job, - submit_job_to_lsf, + submit_job_to_batch_system, process_jobs, cleanup_completed_jobs, cleanup_failed_jobs, @@ -28,10 +28,10 @@ def setUp(self): self.submitting_job = Job.objects.filter(status=Status.SUBMITTING).first() @patch("submitter.toil_submitter.toil_jobsubmitter.ToilJobSubmitter.__init__") - @patch("orchestrator.tasks.submit_job_to_lsf") + @patch("orchestrator.tasks.submit_job_to_batch_system") @patch("batch_systems.lsf_client.lsf_client.LSFClient.submit") @skip("Need to mock memcached lock") - def test_submit_polling(self, job_submitter, submit_job_to_lsf, init): + def test_submit_polling(self, job_submitter, submit_job_to_batch_system, init): init.return_value = None job_submitter.return_value = ( self.current_job.external_id, @@ -39,13 +39,13 @@ def test_submit_polling(self, job_submitter, submit_job_to_lsf, init): self.current_job.working_dir, self.current_job.output_directory, ) - submit_job_to_lsf.return_value = None + submit_job_to_batch_system.return_value = None created_jobs = len(Job.objects.filter(status=Status.CREATED)) process_jobs() - self.assertEqual(submit_job_to_lsf.delay.call_count, created_jobs) - submit_job_to_lsf.reset_mock() + self.assertEqual(submit_job_to_batch_system.delay.call_count, created_jobs) + submit_job_to_batch_system.reset_mock() process_jobs() - self.assertEqual(submit_job_to_lsf.delay.call_count, 0) + self.assertEqual(submit_job_to_batch_system.delay.call_count, 0) @patch("submitter.toil_submitter.toil_jobsubmitter.ToilJobSubmitter.prepare_to_submit") def test_prepare_job(self, prepare_to_submit): @@ -65,7 +65,7 @@ def test_prepare_job(self, prepare_to_submit): def test_submit(self, save_job_info, submit): save_job_info.return_value = None submit.return_value = self.submitting_job.external_id - submit_job_to_lsf(self.submitting_job) + submit_job_to_batch_system(self.submitting_job) self.submitting_job.refresh_from_db() self.assertEqual(self.submitting_job.finished, None) self.assertEqual(self.submitting_job.status, Status.SUBMITTED) From 1e868bf19769ebfa8552b0d85c79f76cdf719f53 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 16:52:09 -0500 Subject: [PATCH 015/235] Slight fixes to SLURM tests --- tests/test_slurm_client.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/tests/test_slurm_client.py b/tests/test_slurm_client.py index f756374d..f079645b 100644 --- a/tests/test_slurm_client.py +++ b/tests/test_slurm_client.py @@ -11,7 +11,7 @@ class TestSLURMClient(TestCase): """ def setUp(self): - self.example_id = 12345678 + self.example_id = "12345678" self.example_job_id = "d736e17e-d67a-4897-901b-decab9942398" self.submit_response = "Submitted batch job {}".format(self.example_id) self.slurm_client = SLURMClient() @@ -35,7 +35,7 @@ def setUp(self): """.format( self.example_id ) - self.exit_reason = "FAILED, tool exit code: 1, batchsystem exit code: 0" + self.exit_reason = "FAILED, tool exit code: 0, batchsystem exit code: 1" self.pend_reason = None @patch("subprocess.run") @@ -45,13 +45,13 @@ def test_submit(self, submit_process): """ command = "ls" args = [] - stdout_file = "stdout.txt" + stdout_file = f"{self.slurm_client.logfileName}" submit_process_obj = Mock() submit_process_obj.stdout = self.submit_response submit_process_obj.returncode = 0 submit_process.return_value = submit_process_obj - slurm_id = self.slurm_client.submit(command, args, stdout_file, self.example_job_id, {}) with self.settings(SLURM_PARTITION=self.example_partion): + slurm_id = self.slurm_client.submit(command, args, stdout_file, self.example_job_id, {}) expected_command = ( [ "sbatch", @@ -62,7 +62,7 @@ def test_submit(self, submit_process): + args + [f"--wrap=exec {command}"] ) - self.assertEqual(slurm_id, self.example_id) + self.assertEqual(f"{slurm_id}", self.example_id) self.assertEqual(submit_process.call_args[0][0], expected_command) @patch("subprocess.run") @@ -72,7 +72,7 @@ def test_submit_with_args(self, submit_process): """ command = "ls" args = [] - stdout_file = "stdout.txt" + stdout_file = f"{self.slurm_client.logfileName}" submit_process_obj = Mock() submit_process_obj.stdout = self.submit_response submit_process_obj.returncode = 0 @@ -93,7 +93,7 @@ def test_submit_with_args(self, submit_process): + args + [f"--wrap=exec {command}"] ) - self.assertEqual(slurm_id, self.example_id) + self.assertEqual(f"{slurm_id}", self.example_id) self.assertEqual(submit_process.call_args[0][0], expected_command) @patch("subprocess.run") @@ -114,7 +114,12 @@ def test_suspend(self, suspend_process): """ Test SLURM suspend """ - suspend_process.side_effect = [f"{self.example_id}", ""] + sacct_process_obj = Mock() + sacct_process_obj.stdout = f"{self.example_id}" + sacct_process_obj.returncode = 0 + scontrol_process_obj = Mock() + scontrol_process_obj.returncode = 0 + suspend_process.side_effect = [sacct_process_obj, scontrol_process_obj] expected_command = ["scontrol", "suspend", f"{self.example_id}"] suspended = self.slurm_client.suspend(self.example_job_id) self.assertEqual(suspend_process.call_args[0][0], expected_command) @@ -125,7 +130,12 @@ def test_resume(self, resume_process): """ Test SLURM resume """ - resume_process.side_effect = [f"{self.example_job_id}", ""] + sacct_process_obj = Mock() + sacct_process_obj.stdout = f"{self.example_id}" + sacct_process_obj.returncode = 0 + scontrol_process_obj = Mock() + scontrol_process_obj.returncode = 0 + resume_process.side_effect = [sacct_process_obj, scontrol_process_obj] expected_command = ["scontrol", "resume", f"{self.example_id}"] resumed = self.slurm_client.resume(self.example_job_id) self.assertEqual(resume_process.call_args[0][0], expected_command) From d7eaf5ac352bc90393b90b96bbe9c6147d47339e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 16:53:01 -0500 Subject: [PATCH 016/235] Increased release number --- ridgeback/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/__init__.py b/ridgeback/__init__.py index 4a1dc255..9483ec48 100644 --- a/ridgeback/__init__.py +++ b/ridgeback/__init__.py @@ -1 +1 @@ -__version__ = "1.36.0" +__version__ = "1.37.0" From 4c585d47bc7360101c883e84ad1cfcc0c1585e0b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 16:56:05 -0500 Subject: [PATCH 017/235] Updated black formatting --- batch_systems/batch_system.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index 2324388a..c15c2411 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -5,9 +5,11 @@ def get_batch_system(): if settings.BATCH_SYSTEM == "LSF": from batch_systems.lsf_client.lsf_client import LSFClient + return LSFClient() elif settings.BATCH_SYSTEM == "SLURM": from batch_systems.slurm_client.slurm_client import SLURMClient + return SLURMClient() else: raise Exception(f"Batch system {settings.BATCH_SYSTEM} not supported, please use either LSF or SLURM") From 74c199c37ea89c51ecc761a1ff2fc43a227c7881 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 7 Feb 2025 17:26:50 -0500 Subject: [PATCH 018/235] Flake8 fixes --- batch_systems/slurm_client/slurm_client.py | 3 +-- pyproject.toml | 3 +++ tests/test_slurm_client.py | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index dbb9c2ad..eec9c208 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -5,7 +5,6 @@ import os import re import subprocess -import json import logging from django.conf import settings from orchestrator.models import Status @@ -96,7 +95,7 @@ def set_walltime(self, expected_limit, hard_limit): walltime_args = walltime_args + [f"--t={expected_limit}"] if hard_limit: self.logger.debug( - "Hard limits are no supported on submission, its configured by the clusters's KillWait and OverTimeLimit parameters" + "Hard limits on submit are no supported, please check the cluster KillWait and OverTimeLimit params" ) return walltime_args diff --git a/pyproject.toml b/pyproject.toml index 55ec8d78..6b6ac676 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,5 @@ [tool.black] line-length = 120 + +[flake8] +ignore = W503 diff --git a/tests/test_slurm_client.py b/tests/test_slurm_client.py index f079645b..20ef1f31 100644 --- a/tests/test_slurm_client.py +++ b/tests/test_slurm_client.py @@ -1,6 +1,5 @@ from django.test import TestCase from mock import patch, Mock -from django.conf import settings from batch_systems.slurm_client.slurm_client import SLURMClient from orchestrator.models import Status From c9ac3f28565e99afe0bfb2cea947f83f0d70b420 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 10 Feb 2025 10:29:07 -0500 Subject: [PATCH 019/235] Fix pyproject.toml --- pyproject.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6b6ac676..55ec8d78 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,2 @@ [tool.black] line-length = 120 - -[flake8] -ignore = W503 From ec6682d4673a93d3f3e916721a1acac3aefc334b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 10 Feb 2025 10:29:38 -0500 Subject: [PATCH 020/235] Remove flake8 test --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c1540e17..9f68237a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,5 @@ script: - source travis_env.sh - coverage run --source='.' manage.py test - coverage report -m --fail-under=75 - - flake8 - black --check . From c2687bdb806df9df6208106e868e2e194452ee01 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 10 Feb 2025 10:46:59 -0500 Subject: [PATCH 021/235] Revert "Remove flake8 test" This reverts commit 9f32eed07b35728d5126ed62a321e0f79d2be914. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9f68237a..c1540e17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,5 +29,6 @@ script: - source travis_env.sh - coverage run --source='.' manage.py test - coverage report -m --fail-under=75 + - flake8 - black --check . From 1f28f9f56c1e32f448c69e3829f9d9edf9e3b2ed Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 10 Feb 2025 10:54:00 -0500 Subject: [PATCH 022/235] Ignore W503 flake errors --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 9834cf7d..3aad9c22 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [flake8] exclude = .git,*migrations* max-line-length = 120 -ignore = E203 +ignore = E203,W503 per-file-ignores = __init__.py:F401 From 91d6fda637326bc118fef189fdcfdd20ad5d345e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 18 Feb 2025 16:24:51 -0500 Subject: [PATCH 023/235] Add dockerfile --- Dockerfile | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..1ef84b9d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ + version.image="1.0.0" \ + source.ridgeback="https://github.com/mskcc/ridgeback" + +ENV DEBIAN_FRONTEND noninteractive +ENV RIDGEBACK_BRANCH master + +RUN apt-get clean && apt-get update -qq \ + # Install dependencies + && apt-get -y install \ + python3 python3-dev python3-pip python3-virtualenv wget \ + libldap2-dev libsasl2-dev libssl-dev libxml2-dev libxslt-dev \ + postgresql postgresql-contrib libpq-dev \ + gawk build-essential nodejs \ + git \ + default-jdk \ + && cd /usr/bin \ + # Install Ridgeback + && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ + && cd /usr/bin/ridgeback \ + # Install python packages + && pip3 install --upgrade pip \ + && python3 -m pip install python-ldap \ + && pip3 install setuptools==57.5.0 \ + && pip install "cython<3.0.0" wheel \ + && pip install "pyyaml==5.4.1" --no-build-isolation \ + && pip3 install -r requirements.txt \ + && pip3 install -r requirements-toil.txt \ No newline at end of file From 4fcd571c7d3b2c0c620f454b7f6540831ca62357 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 18 Feb 2025 17:19:00 -0500 Subject: [PATCH 024/235] Made pip install more consistent --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1ef84b9d..35564f7b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ source.ridgeback="https://github.com/mskcc/ridgeback" ENV DEBIAN_FRONTEND noninteractive +ENV PIP_ROOT_USER_ACTION ignore ENV RIDGEBACK_BRANCH master RUN apt-get clean && apt-get update -qq \ @@ -22,9 +23,8 @@ RUN apt-get clean && apt-get update -qq \ && cd /usr/bin/ridgeback \ # Install python packages && pip3 install --upgrade pip \ - && python3 -m pip install python-ldap \ - && pip3 install setuptools==57.5.0 \ - && pip install "cython<3.0.0" wheel \ - && pip install "pyyaml==5.4.1" --no-build-isolation \ + && pip3 install python-ldap,setuptools==57.5.0 \ + && pip3 install "cython<3.0.0" wheel \ + && pip3 install "pyyaml==5.4.1" --no-build-isolation \ && pip3 install -r requirements.txt \ && pip3 install -r requirements-toil.txt \ No newline at end of file From 5463f07e93b66be68c2b29f1e071a5a1fd77e122 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 4 Mar 2025 12:28:04 -0500 Subject: [PATCH 025/235] Add compose files --- Dockerfile | 44 ++++++------ compose.yaml | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 21 deletions(-) create mode 100644 compose.yaml diff --git a/Dockerfile b/Dockerfile index 35564f7b..695a82da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM openjdk:22-slim LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ version.image="1.0.0" \ @@ -6,25 +6,27 @@ LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ ENV DEBIAN_FRONTEND noninteractive ENV PIP_ROOT_USER_ACTION ignore -ENV RIDGEBACK_BRANCH master +ENV PIP_BREAK_SYSTEM_PACKAGES 1 +ENV RIDGEBACK_BRANCH feature/IRIS_migration -RUN apt-get clean && apt-get update -qq \ - # Install dependencies - && apt-get -y install \ - python3 python3-dev python3-pip python3-virtualenv wget \ +RUN apt-get update \ + # Install dependencies + && apt-get -y --no-install-recommends install \ + python3 python3-pip wget \ libldap2-dev libsasl2-dev libssl-dev libxml2-dev libxslt-dev \ - postgresql postgresql-contrib libpq-dev \ - gawk build-essential nodejs \ - git \ - default-jdk \ - && cd /usr/bin \ - # Install Ridgeback - && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ - && cd /usr/bin/ridgeback \ - # Install python packages - && pip3 install --upgrade pip \ - && pip3 install python-ldap,setuptools==57.5.0 \ - && pip3 install "cython<3.0.0" wheel \ - && pip3 install "pyyaml==5.4.1" --no-build-isolation \ - && pip3 install -r requirements.txt \ - && pip3 install -r requirements-toil.txt \ No newline at end of file + postgresql-client libpq-dev \ + gawk nodejs git build-essential python3-dev \ + # Install Ridgeback + && cd /usr/bin \ + && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ + && cd /usr/bin/ridgeback \ + # Install python packages + && pip3 install --upgrade pip \ + && pip3 install python-ldap setuptools==57.5.0 \ + && pip3 install "pyyaml==5.4.1" --no-build-isolation \ + && pip3 install -r requirements.txt \ + && pip3 install -r requirements-toil.txt \ + # Clean up image + && rm -rf /var/lib/apt/lists/* + + diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 00000000..c6886c75 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,191 @@ +name: 'Ridgeback Services' + +services: + postgres: + image: postgres:17 + restart: on-failure + volumes: + - ./ridgeback_compose/database:/var/lib/postgresql/data/ + environment: + - POSTGRES_USER=${RIDGEBACK_DB_USERNAME} + - POSTGRES_PASSWORD=${RIDGEBACK_DB_PASSWORD} + - POSTGRES_DB=${RIDGEBACK_DB_NAME} + ports: + - ${RIDGEBACK_DB_PORT}:5432 + healthcheck: + test: ["CMD-SHELL", "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'"] + interval: 60s + timeout: 3s + retries: 3 + memcached: + image: memcached:1.6.36 + restart: on-failure + ports: + - ${RIDGEBACK_MEMCACHED_PORT}:11211 + healthcheck: + test: ["CMD", "nc", "-z", "localhost", "11211"] + interval: 10s + timeout: 5s + retries: 3 + rabbitmq: + image: rabbitmq:4.0.6-management-alpine + restart: always + volumes: + - ./ridgeback_compose/rabbitmq/data/:/var/lib/rabbitmq/ + - ./ridgeback_compose/rabbitmq/log/:/var/log/rabbitmq/ + ports: + - ${RIDGEBACK_RABBITMQ_PORT}:5672 + - ${RIDGEBACK_RABBITMQ_MANAGEMENT_PORT}:15672 + environment: + - RABBITMQ_NODENAME=rabbitmq_ridgeback + - RABBITMQ_NODE_IP_ADDRESS=127.0.0.1 + - RABBITMQ_DEFAULT_USER=${RIDGEBACK_RABBITMQ_USERNAME} + - RABBITMQ_DEFAULT_PASS=${RIDGEBACK_RABBITMQ_PASSWORD} + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "check_running"] + interval: 60s + timeout: 3s + retries: 3 + ridgeback_webserver: + iamge: mskcc/ridgeback:${RIDGEBACK_VERSION} + restart: always + volumes: + - ./ridgeback_compose/server:/ridgeback/server + ports: + - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} + command: > + bash -c " + python3 ${RIDGEBACK_PATH}/manage.py wait_for_db && + python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput && + python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput && + python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_POR}T >> /ridgeback_compose/server/boot.log 2>&1" + healthcheck: + test: ["CMD-SHELL", "curl --fail http://localhost:${RIDGEBACK_PORT}/ || exit 1"] + interval: 60s + timeout: 3s + retries: 3 + depends_on: + postgres: + condition: service_healthy + restart: true + memcached: + condition: service_healthy + restart: false + rabbitmq: + condition: service_healthy + restart: false + ridgeback_celery: + iamge: mskcc/ridgeback:${RIDGEBACK_VERSION} + restart: always + user: "${DOCKER_UID}:${DOCKER_GID}" + volumes: + - ${SLURM_BIN_PATH}:/usr/batchsystem/bin + - ${SLURM_ETC}:${SLURM_ETC} + - ${SLURM_LIB_PATH}:${SLURM_LIB_PATH} + - ${SLURM_ETC_PASSWD}:${SLURM_ETC_PASSWD} + - ${SLURM_MUNGE_VAR}:${SLURM_MUNGE_VAR} + - ${SLURM_LIBMUNGE_OBJECT}:${SLURM_LIBMUNGE_OBJECT} + - ./ridgeback_compose/celery/logs:/ridgeback/celery/logs + - ./ridgeback_compose/celery/pids:/ridgeback/celery/pids + - ./ridgeback_compose/celery:/ridgeback/celery + - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} + command: > + bash -c "source ${RIDGEBACK_VENV}/bin/activate && + pip3 install --upgrade pip && + pip3 install --force-reinstall 'setuptools<58.0.0 && + pip3 install -r /usr/bin/ridgeback/requirements.txt && + pip3 install -r requirements-toil.txt && + + echo 'Running orchestrator beat...' && + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator beat \ + --detach \ + -l info \ + -f /ridgeback/celery/logs/ridgeback_beat.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid \ + -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule -detach && + + echo 'Running command queue worker...' && + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + --detach \ + -l info \ + -Q ${RIDGEBACK_COMMAND_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid \ + --concurrency=30 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} && + + echo 'Running action queue worker...'' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + --detach \ + -l info \ + -Q ${RIDGEBACK_ACTION_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid \ + --concurrency=10 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} && + + echo 'Running check status queue worker...'' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + --detach \ + -l info \ + -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid \ + --concurrency=1 && + + echo 'Running submit job queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + --detach \ + -l info \ + -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid \ + --concurrency=1 && + + echo 'Running short queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + --detach \ + -l info \ + -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid \ + --concurrency=10 && + + echo 'Running cleanup queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + --detach \ + -l info \ + -Q ${RIDGEBACK_CLEANUP_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ + --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid \ + --concurrency=2" + healthcheck: + test: ["CMD-SHELL", "sh -c 'source ${RIDGEBACK_VENV}/bin/activate; celery -A orchestrator' status || exit 1"] + interval: 60s + timeout: 3s + retries: 3 + depends_on: + postgres: + condition: service_healthy + restart: true + memcached: + condition: service_healthy + restart: false + rabbitmq: + condition: service_healthy + restart: false + From 46d9f74b482a1a03c0b16e947448723bd896e921 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 4 Mar 2025 19:23:47 -0500 Subject: [PATCH 026/235] Updated psycopg2-binary --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4cc67d1e..a1a696db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ drf-yasg==1.17.1 GitPython==3.1.30 celery==5.2.2 ruamel.yaml<=0.16.5 -psycopg2-binary==2.8.6 +psycopg2-binary==2.9.10 mock==4.0.2 dj-static==0.0.6 django-pymemcache==1.0.0 From 520801f41e2955271ffc1cfbb5966ab20c5654dc Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 4 Mar 2025 19:24:13 -0500 Subject: [PATCH 027/235] Add setting for memcached host --- ridgeback/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index bb9cdcee..2eb69ee2 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -118,7 +118,7 @@ "PORT": DB_PORT, } } - +MEMCACHED_HOST = os.environ.get("RIDGEBACK_MEMCACHED_HOST", "127.0.0.1") MEMCACHED_PORT = os.environ.get("RIDGEBACK_MEMCACHED_PORT", 11211) if ENVIRONMENT == "dev": @@ -132,7 +132,7 @@ CACHES = { "default": { "BACKEND": "djpymemcache.backend.PyMemcacheCache", - "LOCATION": "127.0.0.1:%s" % MEMCACHED_PORT, + "LOCATION": "%s:%s" % (MEMCACHED_HOST, MEMCACHED_PORT), "OPTIONS": { # see https://pymemcache.readthedocs.io/en/latest/apidoc/pymemcache.client.base.html "default_noreply": False From 3339a67424d20598d9d8571bad568bcb408794d6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 4 Mar 2025 19:48:13 -0500 Subject: [PATCH 028/235] Update docker image and revert psycopg2-binary update --- Dockerfile | 4 ++-- requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 695a82da..afa5b912 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:22-slim +FROM ubuntu:22.04 LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ version.image="1.0.0" \ @@ -14,7 +14,7 @@ RUN apt-get update \ && apt-get -y --no-install-recommends install \ python3 python3-pip wget \ libldap2-dev libsasl2-dev libssl-dev libxml2-dev libxslt-dev \ - postgresql-client libpq-dev \ + postgresql-client libpq-dev default-jdk \ gawk nodejs git build-essential python3-dev \ # Install Ridgeback && cd /usr/bin \ diff --git a/requirements.txt b/requirements.txt index a1a696db..4cc67d1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ drf-yasg==1.17.1 GitPython==3.1.30 celery==5.2.2 ruamel.yaml<=0.16.5 -psycopg2-binary==2.9.10 +psycopg2-binary==2.8.6 mock==4.0.2 dj-static==0.0.6 django-pymemcache==1.0.0 From b27f10d17275e131d87d99bc3db4b4dd23233ee3 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 4 Mar 2025 19:49:31 -0500 Subject: [PATCH 029/235] Update dockerfile build branch --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index afa5b912..47ea83c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ ENV DEBIAN_FRONTEND noninteractive ENV PIP_ROOT_USER_ACTION ignore ENV PIP_BREAK_SYSTEM_PACKAGES 1 -ENV RIDGEBACK_BRANCH feature/IRIS_migration +ENV RIDGEBACK_BRANCH feature/IRIS_update RUN apt-get update \ # Install dependencies From 98f8ee964846961fb5991ab4fd284d6339a0fd2b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 13 Mar 2025 13:35:04 -0400 Subject: [PATCH 030/235] Removed elastic apm artifact --- requirements.txt | 1 - ridgeback/settings.py | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/requirements.txt b/requirements.txt index 4cc67d1e..d93b6a02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,6 @@ django-pymemcache==1.0.0 coverage==5.5 flake8==3.9.0 black==22.3.0 -elastic-apm==6.1.3 ddtrace==1.7.3 django-extensions==3.1.1 urllib3==1.26.6 diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 2eb69ee2..88e3d98f 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -39,17 +39,6 @@ SESSION_COOKIE_NAME = os.environ.get("RIDGEBACK_COOKIE_SESSION_NAME", "ridgeback_prod_session") - -ELASTIC_APM = { - # Set the required service name. Allowed characters: - # a-z, A-Z, 0-9, -, _, and space - "SERVICE_NAME": "ridgeback", - # Set the custom APM Server URL (default: http://localhost:8200) - "SERVER_URL": "http://bic-dockerapp01.mskcc.org:8200/", - # Set the service environment - "ENVIRONMENT": ENVIRONMENT, -} - # Application definition INSTALLED_APPS = [ @@ -63,12 +52,10 @@ "orchestrator.apps.OrchestratorConfig", "rest_framework", "drf_yasg", - "elasticapm", "django_extensions", ] MIDDLEWARE = [ - "elasticapm.contrib.django.middleware.TracingMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", From e6296e52547d3d9ae1b473eb4be7dc581f23219e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 13 Mar 2025 13:36:04 -0400 Subject: [PATCH 031/235] Updated dockerfile to build faster and slimmer --- Dockerfile | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 47ea83c9..803f9295 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM python:3.10-slim LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ version.image="1.0.0" \ @@ -11,11 +11,9 @@ ENV RIDGEBACK_BRANCH feature/IRIS_update RUN apt-get update \ # Install dependencies - && apt-get -y --no-install-recommends install \ - python3 python3-pip wget \ - libldap2-dev libsasl2-dev libssl-dev libxml2-dev libxslt-dev \ - postgresql-client libpq-dev default-jdk \ - gawk nodejs git build-essential python3-dev \ + && apt-get -y --no-install-recommends install \ + wget curl libldap2-dev libsasl2-dev libssl-dev libxml2-dev libxslt-dev \ + libpq-dev gawk nodejs git build-essential \ # Install Ridgeback && cd /usr/bin \ && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ @@ -27,6 +25,7 @@ RUN apt-get update \ && pip3 install -r requirements.txt \ && pip3 install -r requirements-toil.txt \ # Clean up image - && rm -rf /var/lib/apt/lists/* + && apt-get -y purge --auto-remove build-essential \ + && rm -rf /var/lib/apt/lists/* From e19b6e5c445c898213731011895024dd4d011d4d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 13 Mar 2025 14:17:07 -0400 Subject: [PATCH 032/235] Added missing ps package --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 803f9295..58c68468 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ ENV RIDGEBACK_BRANCH feature/IRIS_update RUN apt-get update \ # Install dependencies && apt-get -y --no-install-recommends install \ - wget curl libldap2-dev libsasl2-dev libssl-dev libxml2-dev libxslt-dev \ + wget curl libldap2-dev libsasl2-dev procps libssl-dev libxml2-dev libxslt-dev \ libpq-dev gawk nodejs git build-essential \ # Install Ridgeback && cd /usr/bin \ From a28359b30cdccf7706a08affb9439a42a1d51cce Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 18 Mar 2025 19:43:46 -0400 Subject: [PATCH 033/235] Fixed a typo --- submitter/toil_submitter/toil_jobsubmitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 83a9c355..9afe9afe 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -54,7 +54,7 @@ def __init__( self.batch_system_args_env = None if settings.BATCH_SYSTEM == "LSF": self.batch_system_args_env = "TOIL_LSF_ARGS" - elif settings.BATCH_STYSTEM == "SLURM": + elif settings.BATCH_SYSTEM == "SLURM": self.batch_system_args_env = "TOIL_SLURM_ARGS" def prepare_to_submit(self): From 6976bb258a2c3f914f3a4da45a338f89f43e6b98 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 18 Mar 2025 20:16:01 -0400 Subject: [PATCH 034/235] Set workdir for sbatch submission --- batch_systems/slurm_client/slurm_client.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index eec9c208..df68f70e 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -42,6 +42,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): Returns: int: slurm job id """ + work_dir = os.path.dirname(stdout) sbatch_command = ( ["sbatch"] @@ -63,6 +64,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): check=True, stdout=subprocess.PIPE, universal_newlines=True, + cwd=work_dir, env=current_env, ) if process.returncode != 0: From 8d6b5571b513be5e7ba22b5e36ea47cf620948b8 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 18 Mar 2025 20:16:24 -0400 Subject: [PATCH 035/235] Fixed sacct commands --- batch_systems/slurm_client/slurm_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index df68f70e..6edde390 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -262,7 +262,7 @@ def status(self, external_job_id): Returns: tuple: (Ridgeback Status int, extra info) """ - saact_command = ["sacct", f"--jobs={external_job_id}.batch", "--format='jobid,state,exitcode'", "-n", "-P"] + saact_command = ["sacct", f"--jobs={external_job_id}.batch", "--format=jobid,state,exitcode", "-n", "-P"] self.logger.debug("Checking slurm status for job: %s", external_job_id) process = subprocess.run(saact_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) status = self._parse_status(process.stdout, str(external_job_id)) @@ -279,7 +279,7 @@ def _get_job_list(self, job_id): """ slurm_jobs = [] - saact_command = ["sacct", f"--wckeys={job_id}", "--format='jobid'", "-n", "-P"] + saact_command = ["sacct", f"--wckeys={job_id}", "--format=jobid", "-n", "-P"] process = subprocess.run(saact_command, check=True, stdout=subprocess.PIPE, universal_newlines=True) output = process.stdout for single_line in output.strip().split("\n"): From 3a569dcdfacac2803c20d383eca626cf5405b316 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 19 Mar 2025 09:55:03 -0400 Subject: [PATCH 036/235] Handle command as a list --- batch_systems/batch_system.py | 2 +- batch_systems/lsf_client/lsf_client.py | 2 +- batch_systems/slurm_client/slurm_client.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index c15c2411..6f46b9d2 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -36,7 +36,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): Submit command to LSF and store log in stdout Args: - command (str): command to submit + command (list): command to submit job_args (list): Additional options for leader job stdout (str): log file path job_id (str): job_id diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index ed36ed96..5d161612 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -38,7 +38,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): Submit command to LSF and store log in stdout Args: - command (str): command to submit + command (list): command to submit job_args (list): Additional options for leader bsub stdout (str): log file path job_id (str): job_id diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 6edde390..b3b37209 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -33,7 +33,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): Submit command to SLURM and store log in stdout Args: - command (str): command to submit + command (list): command to submit job_args (list): Additional options for leader sbatch stdout (str): log file path job_id (str): job_id @@ -44,13 +44,15 @@ def submit(self, command, job_args, stdout, job_id, env={}): """ work_dir = os.path.dirname(stdout) + command_str = " ".join(command) + sbatch_command = ( ["sbatch"] + self.set_service_queue() + self.set_group(job_id) + self.set_stdout_file(stdout) + job_args - + [f"--wrap=exec {command}"] + + [f"--wrap=exec {command_str}"] ) current_env = os.environ.copy() for k, v in env.items(): From 845ed0c9913c99bd0b985444331d3919ffe4c163 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 19 Mar 2025 09:55:20 -0400 Subject: [PATCH 037/235] Fix slurm status collection --- batch_systems/slurm_client/slurm_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index b3b37209..06fc2a98 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -145,7 +145,7 @@ def _parse_sacct(self, sacct_output_str, external_job_id): for single_sacct_line in output_lines: slurm_job_info = single_sacct_line.strip().split("|") slurm_id = slurm_job_info[0] - if slurm_id == external_job_id: + if slurm_id == f"{external_job_id}.batch": status = slurm_job_info[1] exitcode_batch = slurm_job_info[2].split(":")[0] exitcode_tool = slurm_job_info[2].split(":")[1] From 630c93e1d4567d2b7062397876592c6be11fdf00 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 19 Mar 2025 09:55:37 -0400 Subject: [PATCH 038/235] Upgraded TOIL to 8.0.0 --- requirements-toil.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/requirements-toil.txt b/requirements-toil.txt index aeca5b77..b4d5fbcd 100644 --- a/requirements-toil.txt +++ b/requirements-toil.txt @@ -1,9 +1 @@ -schema-salad<8,>=7 -rdflib<4.3.0,>=4.2.2 -typing-extensions>=4.1.0 -mock==4.0.2 -pytest==4.3.1 -pytest-cov==2.6.1 -pytest-timeout==1.3.3 -git+https://github.com/mskcc/toil.git@5.4.3#egg=toil[cwl] -cwltest +git+https://github.com/DataBiosphere/toil.git@releases/8.0.0#egg=toil[cwl] From 65ab0e705dc9df8d706cbd401b735ae28d94dd89 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 19 Mar 2025 09:57:57 -0400 Subject: [PATCH 039/235] Updated image --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 58c68468..8868c3c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,8 +20,7 @@ RUN apt-get update \ && cd /usr/bin/ridgeback \ # Install python packages && pip3 install --upgrade pip \ - && pip3 install python-ldap setuptools==57.5.0 \ - && pip3 install "pyyaml==5.4.1" --no-build-isolation \ + && pip3 install python-ldap \ && pip3 install -r requirements.txt \ && pip3 install -r requirements-toil.txt \ # Clean up image From 06c4e91617b99472f7531139b1f2aca3af43f4f3 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 15:27:19 -0400 Subject: [PATCH 040/235] Formatting fixes --- submitter/toil_submitter/toil_track_utils.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index 12c5fcc9..eb6188b8 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -384,7 +384,6 @@ def writeFileStream(self, jobStoreID=None, cleanup=False, basename=None, encodin pass # pylint: enable=too-many-arguments - def writeSharedFileStream(self, sharedFileName, isProtected=None, encoding=None, errors=None): pass @@ -433,7 +432,6 @@ def __init__( self.show_cwl_internal = show_cwl_internal # pylint: enable=too-many-instance-attributes - def create_job_id(self, job_store_id, id_prefix_param=None, id_suffix_param=None): """ Create a job id using the Id in the TOIL jobstore with @@ -701,10 +699,10 @@ def main(): """ usage_str = """ - USAGE: - toil_track_utils.py track [job_store_path] [work_dir_path] - toil_track_utils.py snapshot [job_store_path_1] [work_dir_path_1] [job_store_path_2] [work_dir_path_2] - """ + USAGE: + toil_track_utils.py track [job_store_path] [work_dir_path] + toil_track_utils.py snapshot [job_store_path_1] [work_dir_path_1] [job_store_path_2] [work_dir_path_2] + """ if len(sys.argv) not in [4, 6]: print(usage_str) From 7660458041b758ba853bb86338757a4753a630cc Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 15:28:14 -0400 Subject: [PATCH 041/235] Print status name when using console output --- submitter/toil_submitter/toil_track_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index eb6188b8..25656912 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -405,6 +405,9 @@ class ToolStatus(IntEnum): FAILED = 4 UNKNOWN = 5 + def __str__(self): + return self.name + class ToilTrack: """ @@ -676,6 +679,9 @@ def script_track_status(toil_track_obj): jobs_path = toil_track_obj.jobs_path jobs = toil_track_obj.jobs work_log_to_job_id = toil_track_obj.work_log_to_job_id + for single_job_id in jobs: + single_job = jobs[single_job_id] + single_job["status"] = str(single_job["status"]) print(json.dumps(jobs, indent=4, sort_keys=True, default=str)) time.sleep(4) From c4c585e590b5370bea39d684ec6f6538e978b428 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 15:29:04 -0400 Subject: [PATCH 042/235] Adapted functions to work with TOIL version 8 --- submitter/toil_submitter/toil_track_utils.py | 322 +++++++++++++------ 1 file changed, 226 insertions(+), 96 deletions(-) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index 25656912..95df1061 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -11,13 +11,14 @@ import copy import json import glob +import inspect from enum import IntEnum from datetime import datetime from json.decoder import JSONDecodeError from orchestrator.exceptions import StopException from toil.jobStores.fileJobStore import FileJobStore +from toil.version import baseVersion from toil.toilState import ToilState as toil_state -from toil.cwl.cwltoil import CWL_INTERNAL_JOBS from toil.jobStores.abstractJobStore import NoSuchJobException, NoSuchJobStoreException, JobException @@ -27,6 +28,13 @@ JITTER = 5 ATTEMPTS = 5 +TOIL_MAJOR_VERSION = int(baseVersion[0]) + +if TOIL_MAJOR_VERSION >= 8: + from toil.bus import replay_message_bus +else: + from toil.cwl.cwltoil import CWL_INTERNAL_JOBS + def _get_method(file_job_store, method): """ @@ -42,25 +50,32 @@ def _get_method(file_job_store, method): def _check_job_method(file_job_store): """ TOIL Adapter function to check for job existence using method under diffrent - names from TOIL 5.4 and 3.21 + names from TOIL versions 8.0, 5.4, and 3.21 """ - check_function = _get_method(file_job_store, "_checkJobStoreIdExists") - if check_function: - return check_function + default_check_function = _get_method(file_job_store, "_wait_for_exists") - fallback_function = _get_method(file_job_store, "_checkJobStoreId") + if default_check_function: + return default_check_function + + fallback_function = _get_method(file_job_store, "_checkJobStoreIdExists") if fallback_function: return fallback_function + # What about second fallback? Ref - https://knowyourmeme.com/memes/second-breakfast + second_fallback_function = _get_method(file_job_store, "_checkJobStoreId") + + if second_fallback_function: + return second_fallback_function + raise Exception("Unable to check jobs, possible incompatibility with the current TOIL version") def _check_retry_count(job): """ TOIL Adapter function to check for the job retry count using attributes under - diffrent names from TOIL 5.4 and 3.21 + diffrent names from TOIL versions 8.0, 5.4, and 3.21 """ if hasattr(job, "_remainingTryCount"): return getattr(job, "_remainingTryCount") @@ -77,7 +92,7 @@ def _check_retry_count(job): def _check_job_stats(job): """ TOIL Adapter function to check for job status using - the proper attributes from TOIL 5.4 and 3.21 + the proper attributes from TOIL versions 8.0, 5.4, and 3.21 """ disk = None memory = None @@ -124,11 +139,16 @@ def _read_stats_file(stats_path): jobs = stats_json.get("jobs", []) for single_worker, single_job in zip(worker_logs, jobs): worker_text = single_worker.get("text", "") - job_stream = _get_job_stream_path(worker_text) + job_stream = None + job_id = None + if TOIL_MAJOR_VERSION >= 8: + job_id = _get_job_id(worker_text) + else: + job_stream = _get_job_stream_path(worker_text) job_mem = single_job.get("memory") job_cpu = single_job.get("clock") - if job_stream and job_mem and job_cpu: - stats_info.append((job_stream, job_mem, job_cpu)) + if (job_stream or job_id) and job_mem and job_cpu: + stats_info.append((job_id, job_stream, job_mem, job_cpu)) return stats_info @@ -154,10 +174,13 @@ def _resume_job_store(job_store_path, total_attempts): raise Exception("Job store path %s not found" % job_store_path) read_only_job_store_obj = ReadOnlyFileJobStore(job_store_path, total_attempts) read_only_job_store_obj.resume() - read_only_job_store_obj.set_job_cache() - job_store_cache = read_only_job_store_obj.job_cache - root_job = _clean_job_store(read_only_job_store_obj, job_store_cache) - return read_only_job_store_obj, root_job + if TOIL_MAJOR_VERSION >= 8: + return read_only_job_store_obj, read_only_job_store_obj.load_root_job() + else: + read_only_job_store_obj.set_job_cache() + job_store_cache = read_only_job_store_obj.job_cache + root_job = _clean_job_store(read_only_job_store_obj, job_store_cache) + return read_only_job_store_obj, root_job def _load_job_store(job_store, root_job): @@ -166,9 +189,12 @@ def _load_job_store(job_store, root_job): into a TOIL state object and avoid random filesystem issues """ - job_store_cache = job_store.job_cache - toil_state_obj = toil_state(job_store, root_job, jobCache=job_store_cache) - return toil_state_obj + if TOIL_MAJOR_VERSION >= 8: + return None + else: + job_store_cache = job_store.job_cache + toil_state_obj = toil_state(job_store, root_job, jobCache=job_store_cache) + return toil_state_obj def _check_job_state(work_log_path, jobs_path): @@ -205,7 +231,10 @@ def _check_worker_logs(work_dir, work_log_to_job_id, jobs_path): if single_worker_log in work_log_to_job_id: job_id = work_log_to_job_id[single_worker_log] else: - job_id = _check_job_state(single_worker_log, jobs_path) + if TOIL_MAJOR_VERSION >= 8: + job_id = get_job_id_from_worker_log(single_worker_log) + else: + job_id = _check_job_state(single_worker_log, jobs_path) if job_id: worker_dict[job_id] = (single_worker_log, last_modified) return worker_dict @@ -223,53 +252,47 @@ def _get_file_modification_time(file_path): return None -def _get_current_jobs(toil_state_obj): - """ - TOIL Adapter function to get updated jobs - from the toil_state_obj - """ - updated_jobs = toil_state_obj.updatedJobs - if not updated_jobs: - return [] - - job_list = [] - if isinstance(updated_jobs, set): - for single_job in updated_jobs: - job_list.append(single_job[0]) - elif isinstance(updated_jobs, dict): - for single_job in updated_jobs.values(): - job_list.append(single_job[0]) - else: - raise Exception("Unable to check TOIL state, possible incompatibility with the current TOIL version") - return job_list - - def _get_job_display_name(job): """ TOIL adapter to get the display name of the job from TOIL job. Use the field job_name or display_name depending on the TOIL version Example: - job_name: file:///Users/kumarn1/work/ridgeback/tests/test_jobstores/ - test_cwl/sleep.cwl#simpleWorkflow/sleep/sleep + job_name: file:///Users/kumarn1/work/ridgeback/tests/test_jobstores/test_cwl/sleep.cwl#simpleWorkflow/sleep/sleep returns "sleep" When id is not specified in the cwl it will return the name of the cwl Example: - job_name: file:///Users/kumarn1/work/ridgeback/tests/test_jobstores/ - test_cwl/sleep.cwl + job_name: file:///Users/kumarn1/work/ridgeback/tests/test_jobstores/test_cwl/sleep.cwl returns "sleep" """ - job_name = job.jobName - display_name = job.displayName - cwl_path = None - if "cwl" in job_name: - cwl_path = job_name - elif "cwl" in display_name: - cwl_path = display_name + if TOIL_MAJOR_VERSION >= 8: + display_name = None + unit_name = job.unitName + if not unit_name: + unit_name = job.displayName + if ".cwl" in unit_name: + display_name = unit_name.split(".")[-2] + elif "." in unit_name: + split_list = unit_name.split(".") + if split_list[-1].startswith("_"): + display_name = "".join(split_list[-2::]) + else: + display_name = unit_name.split(".")[-1] + else: + display_name = unit_name + return display_name else: - raise Exception("Could not find name in possible values %s %s" % (job_name, display_name)) - job_basename = os.path.basename(cwl_path) - display_name = os.path.splitext(job_basename)[0] - return display_name + job_name = job.jobName + display_name = job.displayName + cwl_path = None + if "cwl" in job_name: + cwl_path = job_name + elif "cwl" in display_name: + cwl_path = display_name + else: + raise Exception("Could not find name in possible values %s %s" % (job_name, display_name)) + job_basename = os.path.basename(cwl_path) + display_name = os.path.splitext(job_basename)[0] + return display_name class ReadOnlyFileJobStore(FileJobStore): @@ -292,8 +315,13 @@ def check_if_job_exists(self, job_store_id): Check if the job exists in the job store """ check_function = _check_job_method(self) + max_tries = 0 + args = inspect.getfullargspec(check_function).args try: - check_function(job_store_id) + if "maxTries" in args: + check_function(job_store_id, maxTries=max_tries) + else: + check_function(job_store_id) return True except NoSuchJobException: return False @@ -301,11 +329,17 @@ def check_if_job_exists(self, job_store_id): def load(self, jobStoreID): if jobStoreID in self.job_cache: return self.job_cache[jobStoreID] - self.check_if_job_exists(jobStoreID) - job_file = self._getJobFileName(jobStoreID) - with open(job_file, "rb") as file_handle: - job = pickle.load(file_handle) - return job + if self.check_if_job_exists(jobStoreID): + if TOIL_MAJOR_VERSION >= 8: + job_file = self._get_job_file_name(jobStoreID) + else: + job_file = self._getJobFileName(jobStoreID) + try: + with open(job_file, "rb") as file_handle: + job = pickle.load(file_handle) + except: + return None + return job def set_job_cache(self): """ @@ -465,12 +499,12 @@ def create_job_id(self, job_store_id, id_prefix_param=None, id_suffix_param=None id_string = "%s-%s-%s" % (id_prefix, job_id, id_suffix) return id_string - def mark_job_as_failed(self, job_id, job_name, job): + def mark_job_as_failed(self, job_id, job): """ Mark a job as failed """ job_dict = self.jobs - if job_name not in CWL_INTERNAL_JOBS or self.show_cwl_internal: + if not self.is_local_job(job): if job_id in job_dict: job_dict[job_id]["status"] = ToolStatus.FAILED if not job_dict[job_id]["finished"]: @@ -495,6 +529,36 @@ def mark_job_as_failed(self, job_id, job_name, job): } job_dict[job_id] = new_job + def mark_job_as_completed(self, job_id, job): + """ + Mark a job as completed + """ + job_dict = self.jobs + if not self.is_local_job(job): + if job_id in job_dict: + job_dict[job_id]["status"] = ToolStatus.COMPLETED + if not job_dict[job_id]["finished"]: + job_dict[job_id]["finished"] = datetime.now() + else: + cores, disk, memory = _check_job_stats(job) + display_name = _get_job_display_name(job) + new_job = { + "name": display_name, + "disk": disk, + "status": ToolStatus.COMPLETED, + "job_stream": None, + "memory_req": memory, + "cores_req": cores, + "cpu_usage": [], + "mem_usage": [], + "started": datetime.now(), + "submitted": datetime.now(), + "last_modified": datetime.now(), + "log_path": None, + "finished": datetime.now(), + } + job_dict[job_id] = new_job + def _update_job_stats(self, job_key, job_mem, job_cpu): """ Parse and update job stats @@ -527,10 +591,13 @@ def check_stats(self, job_store_obj): stats_file_list = job_store_obj.get_stats_files() for single_stats_path in stats_file_list: stats_info = _read_stats_file(single_stats_path) - for job_stream, job_mem, job_cpu in stats_info: - job_key = jobs_path.get(job_stream) - if job_key: - self._update_job_stats(job_key, job_mem, job_cpu) + for job_id, job_stream, job_mem, job_cpu in stats_info: + if job_id: + self._update_job_stats(job_id, job_mem, job_cpu) + else: + job_key = jobs_path.get(job_stream) + if job_key: + self._update_job_stats(job_key, job_mem, job_cpu) def handle_failed_jobs(self, job_store): """ @@ -542,9 +609,11 @@ def handle_failed_jobs(self, job_store): if retry_count is not None: previous_suffix = self.total_attempts - (retry_count + 1) jobstore_id = single_job.jobStoreID - job_id = self.create_job_id(jobstore_id, id_suffix_param=previous_suffix) - job_name = single_job.jobName - self.mark_job_as_failed(job_id, job_name, single_job) + if TOIL_MAJOR_VERSION >= 8: + job_id = jobstore_id + else: + job_id = self.create_job_id(jobstore_id, id_suffix_param=previous_suffix) + self.mark_job_as_failed(job_id, single_job) def handle_restarted_jobs(self, job_store): """ @@ -558,32 +627,92 @@ def handle_restarted_jobs(self, job_store): jobstore_id = single_job.jobStoreID previous_retry_count = self.total_attempts - (retry_count + 1) retry_job_ids[jobstore_id] = previous_retry_count - job_id = self.create_job_id(jobstore_id, id_suffix_param=previous_retry_count) + if TOIL_MAJOR_VERSION >= 8: + job_id = jobstore_id + else: + job_id = self.create_job_id(jobstore_id, id_suffix_param=previous_retry_count) if job_id in job_dict and job_dict[job_id]["status"] != ToolStatus.FAILED: - job_name = single_job.jobName - self.mark_job_as_failed(job_id, job_name, single_job) + self.mark_job_as_failed(job_id, single_job) + + def set_job_stream(self, single_job, job_id): + job_stream = None + if single_job.command: + job_stream = _get_job_stream_path(single_job.command) + if not job_stream: + logger.debug("Could not find job_stream for job %s [%s]", single_job.job_name, job_id) + self.jobs_path[job_stream] = job_id + return job_stream + + def is_local_job(self, job): + if TOIL_MAJOR_VERSION >= 8: + return job.local and self.show_cwl_internal + else: + return job.jobName in CWL_INTERNAL_JOBS and self.show_cwl_internal + + def _get_current_jobs(self, toil_state_obj, job_store): + """ + TOIL Adapter function to get updated jobs + from the toil_state_obj + """ + job_list = [] + + if TOIL_MAJOR_VERSION >= 8: + message_bus = job_store.config.write_messages + if message_bus and os.path.exists(message_bus): + all_job_statuses = replay_message_bus(message_bus) + for job_status in all_job_statuses.values(): + if job_status.is_running(): + single_job = job_store.load(job_status.job_store_id) + if single_job: + job_list.append(single_job) + elif job_status.exit_code == 0: + single_job = job_store.load(job_status.job_store_id) + if single_job: + self.mark_job_as_completed(job_status.job_store_id, single_job) + elif job_status.exit_code > 0: + single_job = job_store.load(job_status.job_store_id) + if single_job: + self.mark_job_as_failed(job_status.job_store_id, single_job) + else: + pass + + return job_list + else: + updated_jobs = toil_state_obj.updatedJobs + if not updated_jobs: + return [] + if isinstance(updated_jobs, set): + for single_job in updated_jobs: + job_list.append(single_job[0]) + elif isinstance(updated_jobs, dict): + for single_job in updated_jobs.values(): + job_list.append(single_job[0]) + else: + raise Exception("Unable to check TOIL state, possible incompatibility with the current TOIL version") + return job_list - def handle_current_jobs(self, toil_state_obj): + def handle_current_jobs(self, toil_state_obj, job_store): """ Check TOIL jobstore for current/new jobs, add new jobs, and collect stats on new jobs """ jobs_dict = self.jobs current_jobs = [] - for single_job in _get_current_jobs(toil_state_obj): - job_name = single_job.jobName - if job_name not in CWL_INTERNAL_JOBS or self.show_cwl_internal: + for single_job in self._get_current_jobs(toil_state_obj, job_store): + if not self.is_local_job(single_job): cores, disk, memory = _check_job_stats(single_job) jobstore_id = single_job.jobStoreID - job_id = self.create_job_id(jobstore_id) + if TOIL_MAJOR_VERSION >= 8: + job_id = jobstore_id + else: + job_id = self.create_job_id(jobstore_id) current_jobs.append(job_id) job_stream = None - if single_job.command: - job_stream = _get_job_stream_path(single_job.command) - if not job_stream: - logger.debug("Could not find job_stream for job %s [%s]", job_name, job_id) - if job_stream and job_id not in jobs_dict: - self.jobs_path[job_stream] = job_id + if TOIL_MAJOR_VERSION < 8: + job_stream = self.set_job_stream(single_job, job_store) + if not job_stream: + continue + if job_id not in jobs_dict: display_name = _get_job_display_name(single_job) new_job = { "name": display_name, @@ -624,16 +753,17 @@ def handle_running_jobs(self): worker_info = _check_worker_logs(self.work_dir, worker_log_to_job_dict, self.jobs_path) for single_job_id in worker_info: worker_log, last_modified = worker_info[single_job_id] - job_obj = job_dict[single_job_id] - if job_obj["status"] == ToolStatus.PENDING or job_obj["status"] == ToolStatus.UNKNOWN: - job_obj["status"] = ToolStatus.RUNNING - if not job_obj["started"]: - job_obj["started"] = datetime.now() - if last_modified: - job_obj["last_modified"] = last_modified - job_obj["log_path"] = worker_log - if worker_log not in worker_log_to_job_dict: - worker_log_to_job_dict[worker_log] = single_job_id + if single_job_id in job_dict: + job_obj = job_dict[single_job_id] + if job_obj["status"] == ToolStatus.PENDING or job_obj["status"] == ToolStatus.UNKNOWN: + job_obj["status"] = ToolStatus.RUNNING + if not job_obj["started"]: + job_obj["started"] = datetime.now() + if last_modified: + job_obj["last_modified"] = last_modified + job_obj["log_path"] = worker_log + if worker_log not in worker_log_to_job_dict: + worker_log_to_job_dict[worker_log] = single_job_id def check_status(self): """ @@ -654,11 +784,11 @@ def check_status(self): if not job_store.check_if_job_exists(root_job_id): logger.warning("Jobstore root not found, toil job may be finished or just starting") toil_state_obj = _load_job_store(job_store, root_job) - if not toil_state_obj: + if not toil_state_obj and TOIL_MAJOR_VERSION < 8: logger.warning("TOIL state is unexpectedly empty") self.handle_failed_jobs(job_store) self.handle_restarted_jobs(job_store) - current_jobs = self.handle_current_jobs(toil_state_obj) + current_jobs = self.handle_current_jobs(toil_state_obj, job_store) self.handle_finished_jobs(current_jobs) self.handle_running_jobs() self.check_stats(job_store) From e4feb012c66b80d6f53f5e6b82829853ae2fda46 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 15:29:34 -0400 Subject: [PATCH 043/235] Added helper functions for new TOIL --- submitter/toil_submitter/toil_track_utils.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index 95df1061..2100b95a 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -123,6 +123,17 @@ def _get_job_stream_path(text): return None +def _get_job_id(text): + """ + TOIL helper function to parse the + job id path from text + """ + job_id = re.search("kind\S*", text) + if job_id: + return job_id[0] + return None + + def _read_stats_file(stats_path): """ TOIL Adapter function to read the stats file @@ -217,6 +228,15 @@ def _check_job_state(work_log_path, jobs_path): return None +def get_job_id_from_worker_log(worker_log_path): + if os.path.exists(worker_log_path): + with open(worker_log_path) as worker_log: + for single_line in worker_log: + if "Working on job" in single_line: + return _get_job_id(single_line) + return None + + def _check_worker_logs(work_dir, work_log_to_job_id, jobs_path): """ Check the work directory for worker logs and report From ccdf87e49d9c28bdfc227b8fa7b738cbe3c4249a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 15:30:07 -0400 Subject: [PATCH 044/235] Added TOIL 8.0.0 test data --- tests/data/toil_8.0.0.tar.gz | Bin 0 -> 262962 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 tests/data/toil_8.0.0.tar.gz diff --git a/tests/data/toil_8.0.0.tar.gz b/tests/data/toil_8.0.0.tar.gz new file mode 100755 index 0000000000000000000000000000000000000000..1d01ddb86d8e5c6ccf60f5bf4d89b4c316f0ed10 GIT binary patch literal 262962 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

9#(nA7d4f#7L6a&D{V4et* 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

4vodN6JK{W6>XVY+u})4G~HH`IB9 zWGD?`=uro|kcuUzP6_i>sr)p-RS{ei35Ew5oqR|0k9?dEVwRO6$7v*Zlx%SHYloAP zN>jh+!{#Qg7F#)ajOCwmCbrCRr{&rRIQ3YGLbh~0zBKDSHU5++6NT``DUUe6q#4ChaZ&4Tc?Iu=2`5T{t9Y z*vgVH$wdz*WH|8-rZb8HrC8!-!uu&#SCdhc$jjEGRTt@G6Hycv0_qfralUGAh?e-tTdg}< zY8IUf`612EG;K1Wgb77D#oCOauh`fxxo1_A0a7VGzk6 z2bMH(2J{1x)Sj(sqg;m>_By1j$O~~Gs8t>pb9#Xda<3(DL~2R8SRRfXd`NF;&rnR4 z!69i_bC2l5MH&DKLlB}! z2I38-C^yJUzSki3ZdIE>9gTuuGQlqnYGLecG1^u9cqymoD9;SB{3yXzUsW;awlnO- zUX08B72pktLocN@y$ST-gL*^pimIr&QHpJiSnn#yrGz3I7T%;cE z#^b^Wd669m=8+NS2n!h`aiQ8O;(=)qxFu4oXWu={r_rQWR4vYM*oAM7T&blZ??af+ zOQ+k?kma>MDC#?{CkN@lJ7&jhB3A}YO#Ri}?6?QFe(s0nEb+kB>nNBywc5PaYX4&n z9@zGSnFgLfex-R|+pAeEY);R-crAEhSISrv>;9d1Quwm%A~_JZOxTm4RJIzal_;5r zot=iU8MKRqn;b@~0r?h`N2T@@l_-NoL41YPZ~`Wj94PvGEI5DIZc(2=VOAsCgRrv} z#0vnD>9p^`B-3_6W`K+h39Xt@~|kEG`&Ozii|kv&ElJJ{Zn3-m`};q47>iw z#0c24PVQBYzQ#>y$9>hI*W|?0DtEy11%BRr)xprD&hXtUGA~qQSo7l zdSfX!HQf4HuZcMhtvoKJb=Sh$Hq0%Y^SNNE7Zh3(XQQuujiOh2kZ_|?&@e^RAcw- za$rJeX`t3fikcogF5r6AE70+}T4QQ(o0K)y$>a}cCLdfLbNZa!kZ}6YYX)JIbXH`% zd$j*|s%C~KhIlui2t3>~=+F!6?2^30!xA~^LSW`1>ZE*f#Q{p}Zos21_u#3c$_3&` z!ja_hct!YIWKe%jCi3y2DQ0w(OLHv=$8_~7={&Qp4khAxYVMMJN$NIdHeb@&*upS< zr=xN)+&o9cces7Ucj%{tqoJElLdN5BY#QL|?dsb~9-ukB`nDb;{Wh-1+HNgpZf-3% z?$&bV?AGeM!Li(0yaie_6BEIDFC12jRk^;1SLoMi#}atjPyE!`$F}Wbp`x;dEtN0k zAKQdi9N8~6sdc0MEaDR>gbj8+6!~0U+Y;hZ8K#I0Sf(8g8Cjq4K9YoC=M3!zUd%VZ zUgl>gR={#o49Aic3ZPUr{AR0BW76mzql1IgPk6x;bAl7%MkI46>ta&&MU0nB>>EHA z*ecHl)i6KZL`{O%{w(f21@~=6u7e$9Gjn(|vjeQy+hZiZjtat-l#XQi`|KkN#qQ!j zD4JG47)mBA)`3kdeikA{5Qp{&UMyId$D7;$jI*P|uL@I`?pD>VX4%IBYx!9L5>cmx zn>@jxBYYwxVMkz}QLS(Bd40M8sz(BI9PdAM0%sncqDi(Lv;6KPd7aKlQlxY&=i#00#vQnYItG)$+yBw^$Tkz=H16DvsJwe4DnXc+5COSVSDOM&1glQ0(vsSy@qMuZbSh`$gwQBo-5h)*veknC6La%7?D83Z%?0AcyjA ziQ`E%Oyb8niFUDEEy-FG#prNSuR{9|HaA$`JvkB;=^~n*DB;&+Ald_~ zR*tAv5(fZ1k^>v7tEA#s2Q Qp}Fi&DiaB%SrFTLM{dbjI04R!yxV0h8|@25pt( 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 z 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�u%o0qz!>XTk~d4p8_3Q#2Jz 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%0D}$^@h4%w1eKYp}4H)cWbxZJSv=Ccf*AM zrRVxRv$^$-wySz-OZIWSMcx0=;t~5%!>%{U5Boj*Df(0NY+~g~5Pc!Rx3_|JAd$OZ;G0t3Y2j zah%=8Gnp$%I#Z6kf4Edy(3EBcEtsHL#&u^eXYMA zjgGh|h~2RkDAx6X-VNGEsirjp6ut3-K+uoZi@5*HmG)|@1)@8L@`embFJ;twO_s^I z(qvC%ErtW+EKgOR7&UgjuSI0&{qfvV30E~x2@zEx_cc=)bYW~7``@UT!0_-J;jD7S zm`?h$L{L@T&8`rjN!Fm#aS$K*DdE6xk4cES#BA*55J>&Of$<1k=K1SpS$5HBXVbB5 zJ+u>^p4*TFrZ3W^cbAdKnGg)cI2r|o8!ku;{~h_5-t~gZAC1`^*Py^u2g>I)aL}-# zS?hwU`q~q#eS6lw4ojD65_tAI#`KBs$>NIZLT@TijNmixdQx%vZ;5L9)+`{-GS3m( z-|v-K?%l63h2+85s)gh`OySm$Cob7Y_4o);C*r&8w!w<+=NF$^I#<&ov-2OmV}2TX(x9&y1rKgjFSRU0YTUAJf3tN($U(XT zE&|EHjNIZXDy+Q&_`wX+qa)ox55A$k`x@SJ45?_&8pf|f|3x{`zH2!4{EZ=N99g&* zw%|h}r>|*71?6v8@Ve()ppy_51VEz+WMqyZq$b^=c=B|>gxJFP@;{S-d-0k(W~GGs zP?jD`yptW%RD$!?o839~v8hkPgdh^20(uB=iHi!S2zx7i#a2U6FpZy1?sa0Kea!H_ z-%eMf;K8>sAlsOkg`C2|g24OB`TjT5ks7NPoVLd9--Us7{Fl#dvHqWYE=CD1^UQ_Y z74M=t3{2Pdvjs=cnh9^M1;?_MV!kFkewkQvCYO(eIg{+sK5hd%1tH6>fWFr}0=70@ zbOICAT_o2T<;?!9CLA?}<9t5kxJAHGhkh&ZVYVjR0x|r*=yvvYgxbIUHxHZ0yDJg~ zb{vPeF+MT`J?c;oQHIW#Tf}8w3B^cJPj;+AONIt(xY0yRme+)RRPAR84%N|WkxlXH zTemriO}2%_3w-fEnHoMdc#Mpbv5I62K@vymg z+PZt*O?Jf+%8hOL97o}LK@RIDdHh82o2oPyyCPm03o2vPV&u`;b89;{U4 zdLc5XgG!0$e!GhI-}{`Z6%+SU!NoWzQ3OYtIOkCYJ~_W3q%F`#u;FM7xI%U4lqLz= zqW3iII60Oli({OtX|2$BYTKjUNNr|

F*DjyD@?Xu-SR)K5iP_^=sCVMWc^ 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 Date: Tue, 1 Apr 2025 18:16:58 -0400 Subject: [PATCH 045/235] Update toil 8.0.0 test dataset --- tests/data/toil_8.0.0.tar.gz | Bin 262962 -> 262886 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 tests/data/toil_8.0.0.tar.gz diff --git a/tests/data/toil_8.0.0.tar.gz b/tests/data/toil_8.0.0.tar.gz old mode 100755 new mode 100644 index 1d01ddb86d8e5c6ccf60f5bf4d89b4c316f0ed10..e46d1032695d29357612a70ad9f48b4efbaf0a0d GIT binary patch delta 229126 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 z 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≈k*iA>S7)4mDZeoZDtgLV_zS%jz97>t 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⩔NovF|FW`;q$0DRJ)lRVp|6Q-puVgBe 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‏4&uwS~H| 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 delta 231308 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

dRFE$aQADqw)N$0m7Zpv22UdF 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

1A7MS)63MK$bV}78@}J{bl*b1 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�hejhf#|9fleH2MWJuab-RA~gL`br)x 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 HKE(e5pEMbR From 5a2eb7247ea6b0614e483bb1342a4e86dca7ec26 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:17:26 -0400 Subject: [PATCH 046/235] Increased length for job id --- .../migrations/0019_auto_20250401_1659.py | 18 ++++++++++++++++++ orchestrator/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 orchestrator/migrations/0019_auto_20250401_1659.py diff --git a/orchestrator/migrations/0019_auto_20250401_1659.py b/orchestrator/migrations/0019_auto_20250401_1659.py new file mode 100644 index 00000000..8b08f6d4 --- /dev/null +++ b/orchestrator/migrations/0019_auto_20250401_1659.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2025-04-01 16:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orchestrator', '0018_auto_20241114_1618'), + ] + + operations = [ + migrations.AlterField( + model_name='commandlinetooljob', + name='job_id', + field=models.CharField(max_length=50), + ), + ] diff --git a/orchestrator/models.py b/orchestrator/models.py index 2bca2f24..8ec6008a 100755 --- a/orchestrator/models.py +++ b/orchestrator/models.py @@ -263,7 +263,7 @@ class CommandLineToolJob(BaseModel): submitted = models.DateTimeField(blank=True, null=True) finished = models.DateTimeField(blank=True, null=True) job_name = models.CharField(max_length=100) - job_id = models.CharField(max_length=20) + job_id = models.CharField(max_length=50) details = JSONField(blank=True, null=True) def get_aware_datetime(self, date_str): From fb182af54ff812848b3dc789c3df844f1b5f3ef0 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:19:49 -0400 Subject: [PATCH 047/235] Update to handle SLURM pending jobs --- batch_systems/slurm_client/slurm_client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 06fc2a98..47bbfb2d 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -150,6 +150,11 @@ def _parse_sacct(self, sacct_output_str, external_job_id): exitcode_batch = slurm_job_info[2].split(":")[0] exitcode_tool = slurm_job_info[2].split(":")[1] sacct_record = (slurm_id, status, exitcode_batch, exitcode_tool) + if slurm_id == f"{external_job_id}" and not sacct_record: + status = slurm_job_info[1] + exitcode_batch = slurm_job_info[2].split(":")[0] + exitcode_tool = slurm_job_info[2].split(":")[1] + sacct_record = (slurm_id, status, exitcode_batch, exitcode_tool) if not sacct_record: self.logger.error(f"Error - sacct command could not find job {external_job_id}") return sacct_record From d33f8c764e6c52ed08c7a83eedb2e4e3060799d2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:20:23 -0400 Subject: [PATCH 048/235] Update the toil command for 8.0.0 --- submitter/toil_submitter/toil_jobsubmitter.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 9afe9afe..833d20ab 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -274,9 +274,9 @@ def _command_line(self): "--stats", "--cleanWorkDir", "onSuccess", - "--debug", "--disableProgress", "--doubleMem", + "True", "--disableCaching", "--preserve-environment", "PATH", @@ -295,6 +295,7 @@ def _command_line(self): "--not-strict", "--runCwlInternalJobsOnWorkers", "--realTimeLogging", + "True", "--jobStore", self.job_store_dir, "--tmpdir-prefix", @@ -305,6 +306,8 @@ def _command_line(self): self.job_outputs_dir, "--maxLocalJobs", "500", + "--no-prepull", + "--reference-inputs", ] else: command_line = [ @@ -322,9 +325,9 @@ def _command_line(self): "--stats", "--cleanWorkDir", "onSuccess", - "--debug", "--disableProgress", "--doubleMem", + "True", "--disableCaching", "--preserve-environment", "PATH", @@ -344,6 +347,7 @@ def _command_line(self): "--not-strict", "--runCwlInternalJobsOnWorkers", "--realTimeLogging", + "True", "--jobStore", self.job_store_dir, "--tmpdir-prefix", @@ -354,6 +358,8 @@ def _command_line(self): self.job_outputs_dir, "--maxLocalJobs", "500", + "--no-prepull", + "--reference-inputs", ] if self.resume_jobstore: command_line.extend(["--restart", self.app_location]) From f71f6b9f5fef9f0a7234f2a6aa548e8a2add9d0b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:20:46 -0400 Subject: [PATCH 049/235] Separate out the bus function for easier mocking --- submitter/toil_submitter/toil_track_utils.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index 2100b95a..1e88a1b7 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -272,6 +272,13 @@ def _get_file_modification_time(file_path): return None +def _get_bus_path(job_store): + """ + TOIL helper function to get leader bus path, only supported for TOIL >= 8.0.0 + """ + return job_store.config.write_messages + + def _get_job_display_name(job): """ TOIL adapter to get the display name of the job from TOIL job. @@ -677,7 +684,7 @@ def _get_current_jobs(self, toil_state_obj, job_store): job_list = [] if TOIL_MAJOR_VERSION >= 8: - message_bus = job_store.config.write_messages + message_bus = _get_bus_path(job_store) if message_bus and os.path.exists(message_bus): all_job_statuses = replay_message_bus(message_bus) for job_status in all_job_statuses.values(): From 0091907ddf112836167a317a4a1746afedff1a6d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:22:07 -0400 Subject: [PATCH 050/235] Update tests to work with TOIL 8.0.0 --- tests/test_commandline.py | 48 +++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/tests/test_commandline.py b/tests/test_commandline.py index 335ef142..9497b168 100644 --- a/tests/test_commandline.py +++ b/tests/test_commandline.py @@ -5,6 +5,7 @@ import os from shutil import unpack_archive, copytree, copy import tempfile +from mock import patch from django.test import TestCase, override_settings import toil from orchestrator.models import Job, Status, PipelineType, CommandLineToolJob @@ -33,7 +34,7 @@ def get_toil_mock(self, toil_version): def setUp(self): Job.objects.all().delete() self.toil_version = toil.version.baseVersion - if self.toil_version not in ["3.21.0", "5.4.0a1"]: + if self.toil_version not in ["3.21.0", "5.4.0a1", "8.0.0"]: raise Exception("TOIL version: %s not supported" % self.toil_version) self.mock_dir = tempfile.TemporaryDirectory() self.job = Job( @@ -55,17 +56,24 @@ def mock_track(self, run_type): """ Mock track using TOIL snapshots """ + mock_data_path = os.path.join(self.mock_full_path, run_type) first_jobstore = os.path.join(mock_data_path, "0", "jobstore") first_work = os.path.join(mock_data_path, "0", "work") - second_jobstore = os.path.join(mock_data_path, "1", "jobstore") - second_work = os.path.join(mock_data_path, "1", "work") - with tempfile.TemporaryDirectory() as tmpdir: - self.check_status(first_jobstore, first_work, tmpdir) - with tempfile.TemporaryDirectory() as tmpdir: - self.check_status(second_jobstore, second_work, tmpdir) + if self.toil_version == "8.0.0": + with tempfile.TemporaryDirectory() as tmpdir: + bus_path = os.path.join(mock_data_path, "0", "bus") + self.check_status(first_jobstore, first_work, tmpdir, bus_path) + else: + second_jobstore = os.path.join(mock_data_path, "1", "jobstore") + second_work = os.path.join(mock_data_path, "1", "work") + with tempfile.TemporaryDirectory() as tmpdir: + self.check_status(first_jobstore, first_work, tmpdir, None) + with tempfile.TemporaryDirectory() as tmpdir: + self.check_status(second_jobstore, second_work, tmpdir, None) - def check_status(self, jobstore, work_dir, tmp_dir): + @patch("submitter.toil_submitter.toil_track_utils._get_bus_path") + def check_status(self, jobstore, work_dir, tmp_dir, bus_path, get_bus_path): """ Check status of command line jobs """ @@ -74,6 +82,10 @@ def check_status(self, jobstore, work_dir, tmp_dir): tmp_jobstore = os.path.join(tmp_dir, "jobstore") new_work_dir = os.path.join(tmp_work_dir, job_id) new_jobstore = os.path.join(tmp_jobstore, job_id) + if bus_path: + new_bus_path = os.path.join(tmp_dir, "bus") + copy(bus_path, new_bus_path) + get_bus_path.return_value = new_bus_path copytree(jobstore, new_jobstore) copytree(work_dir, new_work_dir) with override_settings( @@ -90,7 +102,10 @@ def test_running(self): self.mock_track("running") mock_num_completed = 0 mock_num_running = 0 - if self.toil_version == "3.21.0": + if self.toil_version == "8.0.0": + mock_num_completed = 1 + mock_num_running = 2 + elif self.toil_version == "3.21.0": mock_num_completed = 1 mock_num_running = 2 elif self.toil_version == "5.4.0a1": @@ -107,24 +122,27 @@ def test_failed(self): Test if failed jobs are properly parsed """ self.mock_track("failed") - mock_num_failed = 2 + if self.toil_version == "8.0.0": + mock_num_failed = 3 + else: + mock_num_failed = 2 num_failed = CommandLineToolJob.objects.filter(status=(Status.FAILED)).count() self.assertEqual(num_failed, mock_num_failed) def test_details_set(self): """ - Test if the metadata is being set for commandLineJObs + Test if the metadata is being set for commandLineJobs """ self.mock_track("running") first_running_job = CommandLineToolJob.objects.filter(status=(Status.RUNNING)).first() details = first_running_job.details self.assertIsNotNone(details) self.assertIsNotNone(details["cores_req"]) - self.assertIsNotNone(details["cpu_usage"]) - self.assertIsNotNone(details["job_stream"]) + self.assertTrue("cpu_usage" in details) + self.assertTrue("job_stream" in details) self.assertIsNotNone(details["last_modified"]) - self.assertIsNotNone(details["log_path"]) - self.assertIsNotNone(details["mem_usage"]) + self.assertTrue("log_path" in details) + self.assertTrue("mem_usage" in details) self.assertIsNotNone(details["memory_req"]) def test_hanging_toil_leader_not_running(self): From afd716fee3934301dae056e83b92c514cdb620be Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:22:32 -0400 Subject: [PATCH 051/235] Update tests to submit with command as a list --- tests/test_slurm_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_slurm_client.py b/tests/test_slurm_client.py index 20ef1f31..ce005f98 100644 --- a/tests/test_slurm_client.py +++ b/tests/test_slurm_client.py @@ -50,7 +50,7 @@ def test_submit(self, submit_process): submit_process_obj.returncode = 0 submit_process.return_value = submit_process_obj with self.settings(SLURM_PARTITION=self.example_partion): - slurm_id = self.slurm_client.submit(command, args, stdout_file, self.example_job_id, {}) + slurm_id = self.slurm_client.submit([command], args, stdout_file, self.example_job_id, {}) expected_command = ( [ "sbatch", @@ -81,7 +81,7 @@ def test_submit_with_args(self, submit_process): args = self.slurm_client.set_walltime(expected_limit, None) args.extend(self.slurm_client.set_memlimit(mem_limit)) with self.settings(SLURM_PARTITION=self.example_partion): - slurm_id = self.slurm_client.submit(command, args, stdout_file, self.example_job_id, {}) + slurm_id = self.slurm_client.submit([command], args, stdout_file, self.example_job_id, {}) expected_command = ( [ "sbatch", From 3342811adf5b054c81d6b6087188fd6c599ea7b6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:28:17 -0400 Subject: [PATCH 052/235] Added black formatting --- orchestrator/migrations/0019_auto_20250401_1659.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestrator/migrations/0019_auto_20250401_1659.py b/orchestrator/migrations/0019_auto_20250401_1659.py index 8b08f6d4..ef916148 100644 --- a/orchestrator/migrations/0019_auto_20250401_1659.py +++ b/orchestrator/migrations/0019_auto_20250401_1659.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('orchestrator', '0018_auto_20241114_1618'), + ("orchestrator", "0018_auto_20241114_1618"), ] operations = [ migrations.AlterField( - model_name='commandlinetooljob', - name='job_id', + model_name="commandlinetooljob", + name="job_id", field=models.CharField(max_length=50), ), ] From 90be9dd05e016bd0697deba85eed282935fffb8b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:28:54 -0400 Subject: [PATCH 053/235] Updated python versions for test image --- .travis.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c1540e17..264595a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,10 @@ addons: postgresql: "11" apt: packages: - - postgresql-11 - - postgresql-client-11 + - postgresql-11 + - postgresql-client-11 python: - - "3.8" + - "3.10" before_install: - sudo apt-get update - sudo apt-get --yes remove postgresql\* @@ -31,4 +31,3 @@ script: - coverage report -m --fail-under=75 - flake8 - black --check . - From 6eae2dbe6548a459b8111577966b177ed0a5beab Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:44:53 -0400 Subject: [PATCH 054/235] Update github actions container --- .github/workflows/github-actions.yml | 51 +++++++++++++--------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index a2a710b8..93e8760c 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -1,9 +1,7 @@ on: push - jobs: build: - runs-on: ubuntu-latest services: @@ -18,33 +16,30 @@ jobs: # needed because the postgres container does not provide a healthcheck options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v4 - with: - python-version: 3.9 - - name: Install dependencies - run: | - pip install pip==19.3.1 - pip install --force-reinstall 'setuptools<58.0.0' - pip install -r requirements.txt - pip install -r requirements-toil.txt - #python manage.py migrate - - name: Run migrations - run: python manage.py migrate - - name: Run test - #run: python manage.py test - run: | - coverage run --source='.' manage.py test - coverage report -m --fail-under=75 - - name: Run flake8 - uses: suo/flake8-github-action@releases/v1 - with: - checkName: 'build' # NOTE: this needs to be the same as the job name + - uses: actions/checkout@v2 + - name: Set up Python 3.10 + uses: actions/setup-python@v4 + with: + python-version: 3.10 + - name: Install dependencies + run: | + pip install pip==19.3.1 + pip install -r requirements.txt + pip install -r requirements-toil.txt + - name: Run migrations + run: python manage.py migrate + - name: Run test + #run: python manage.py test + run: | + coverage run --source='.' manage.py test + coverage report -m --fail-under=75 + - name: Run flake8 + uses: suo/flake8-github-action@releases/v1 + with: + checkName: "build" # NOTE: this needs to be the same as the job name env: - #database env variables + #database env variables RIDGEBACK_DB_NAME: github_actions RIDGEBACK_DB_PASSWORD: postgres RIDGEBACK_DB_USERNAME: postgres @@ -69,7 +64,7 @@ jobs: RIDGEBACK_RABBITMQ_USERNAME: sample_username RIDGEBACK_RABBITMQ_PASSWORD: sample_password - #pipeline env variables + #pipeline env variables ARGOS_JOB_STORE_ROOT: /sample_path ARGOS_WORK_DIR_ROOT: /sample_path From 68d56f1b2579fb9012c9b1cf235a832e7bbdc77b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:45:15 -0400 Subject: [PATCH 055/235] Flake8 fixes --- submitter/toil_submitter/toil_track_utils.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index 1e88a1b7..25635425 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -128,7 +128,7 @@ def _get_job_id(text): TOIL helper function to parse the job id path from text """ - job_id = re.search("kind\S*", text) + job_id = re.search(r"kind\S*", text) if job_id: return job_id[0] return None @@ -284,11 +284,11 @@ def _get_job_display_name(job): TOIL adapter to get the display name of the job from TOIL job. Use the field job_name or display_name depending on the TOIL version Example: - job_name: file:///Users/kumarn1/work/ridgeback/tests/test_jobstores/test_cwl/sleep.cwl#simpleWorkflow/sleep/sleep + job_name: file:///Users/kumarn1/cwl/test_jobstore/sleep.cwl#simpleWorkflow/sleep/sleep returns "sleep" When id is not specified in the cwl it will return the name of the cwl Example: - job_name: file:///Users/kumarn1/work/ridgeback/tests/test_jobstores/test_cwl/sleep.cwl + job_name: file:///Users/kumarn1/cwl/test_jobstore/sleep.cwl returns "sleep" """ if TOIL_MAJOR_VERSION >= 8: @@ -364,7 +364,7 @@ def load(self, jobStoreID): try: with open(job_file, "rb") as file_handle: job = pickle.load(file_handle) - except: + except OSError: return None return job From 9db8ad09358d9d577eaa80e7e0c1fdf206fdddfd Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:45:46 -0400 Subject: [PATCH 056/235] Cleanup travis test image --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 264595a4..214dd3b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,6 @@ before_install: - sudo service postgresql restart 11 install: - psql -p 5433 -c 'create database travis_ci_test;' -U postgres - - pip install --force-reinstall 'setuptools<58.0.0' - pip install -r requirements.txt - pip install -r requirements-toil.txt - source travis_env.sh From 90e79ce163d02c3dd63750e1439eaf8fc9c0b348 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 18:58:47 -0400 Subject: [PATCH 057/235] Update test build images --- .github/workflows/github-actions.yml | 2 +- .travis.yml | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 93e8760c..0ecab6d8 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Python 3.10 uses: actions/setup-python@v4 with: - python-version: 3.10 + python-version: "3.10" - name: Install dependencies run: | pip install pip==19.3.1 diff --git a/.travis.yml b/.travis.yml index 214dd3b9..5793d303 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,24 @@ language: python sudo: required +dist: jammy os: - linux services: - postgresql addons: - postgresql: "11" + postgresql: "14" apt: packages: - - postgresql-11 - - postgresql-client-11 + - postgresql-14 + - postgresql-client-14 python: - "3.10" before_install: - sudo apt-get update - sudo apt-get --yes remove postgresql\* - - sudo apt-get install -y postgresql-11 postgresql-client-11 - - sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf - - sudo service postgresql restart 11 + - sudo apt-get install -y postgresql-14 postgresql-client-14 + - sudo cp /etc/postgresql/14/main/pg_hba.conf + - sudo service postgresql restart 14 install: - psql -p 5433 -c 'create database travis_ci_test;' -U postgres - pip install -r requirements.txt From f7e54b08fc2ba8539927e19c6511732ddbc21ae9 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 19:04:42 -0400 Subject: [PATCH 058/235] Downgrade postgresql in travis test --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5793d303..05db9f1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,19 +6,19 @@ os: services: - postgresql addons: - postgresql: "14" + postgresql: "11" apt: packages: - - postgresql-14 - - postgresql-client-14 + - postgresql-11 + - postgresql-client-11 python: - "3.10" before_install: - sudo apt-get update - sudo apt-get --yes remove postgresql\* - - sudo apt-get install -y postgresql-14 postgresql-client-14 - - sudo cp /etc/postgresql/14/main/pg_hba.conf - - sudo service postgresql restart 14 + - sudo apt-get install -y postgresql-11 postgresql-client-11 + - sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf + - sudo service postgresql restart 11 install: - psql -p 5433 -c 'create database travis_ci_test;' -U postgres - pip install -r requirements.txt From 9039ec341ea540a63413036915a387f06e9ded7b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 20:05:31 -0400 Subject: [PATCH 059/235] Simplify postgres installation on travis --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05db9f1a..02da1909 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,20 +5,10 @@ os: - linux services: - postgresql -addons: - postgresql: "11" - apt: - packages: - - postgresql-11 - - postgresql-client-11 python: - "3.10" before_install: - sudo apt-get update - - sudo apt-get --yes remove postgresql\* - - sudo apt-get install -y postgresql-11 postgresql-client-11 - - sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf - - sudo service postgresql restart 11 install: - psql -p 5433 -c 'create database travis_ci_test;' -U postgres - pip install -r requirements.txt From 5a4e87a5767352350e4beab42f1e4f19d14ece6e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 20:20:47 -0400 Subject: [PATCH 060/235] More travis image updates --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 02da1909..c26aa45b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,17 @@ os: - linux services: - postgresql +addons: + postgresql: "14" + apt: + packages: + - postgresql-14 + - postgresql-client-14 python: - "3.10" before_install: - sudo apt-get update + - sudo service postgresql restart 14 install: - psql -p 5433 -c 'create database travis_ci_test;' -U postgres - pip install -r requirements.txt From d9c654a8497cea4d01a6615f90a1cd521e9c4a04 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 20:30:39 -0400 Subject: [PATCH 061/235] Set postgres port env --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index c26aa45b..151d5147 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,9 @@ sudo: required dist: jammy os: - linux +env: + global: + - PGPORT=5433 services: - postgresql addons: From 006de10359febfdea3f177ba018090834f74a73a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 20:33:51 -0400 Subject: [PATCH 062/235] Update postgres port --- .travis.yml | 5 +---- travis_env.sh | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 151d5147..7966b9d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,6 @@ sudo: required dist: jammy os: - linux -env: - global: - - PGPORT=5433 services: - postgresql addons: @@ -20,7 +17,7 @@ before_install: - sudo apt-get update - sudo service postgresql restart 14 install: - - psql -p 5433 -c 'create database travis_ci_test;' -U postgres + - psql -p 5432 -c 'create database travis_ci_test;' -U postgres - pip install -r requirements.txt - pip install -r requirements-toil.txt - source travis_env.sh diff --git a/travis_env.sh b/travis_env.sh index acb499ea..262606c6 100644 --- a/travis_env.sh +++ b/travis_env.sh @@ -3,7 +3,7 @@ export RIDGEBACK_DB_NAME=travis_ci_test export RIDGEBACK_DB_USERNAME=postgres export RIDGEBACK_DB_PASSWORD= -export RIDGEBACK_DB_PORT=5433 +export RIDGEBACK_DB_PORT=5432 ### Set general env variable From 5cf382396978f001a7752068f51420b874206629 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 1 Apr 2025 21:16:30 -0400 Subject: [PATCH 063/235] Update test for tools without a log path --- tests/test_commandline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_commandline.py b/tests/test_commandline.py index 9497b168..706af674 100644 --- a/tests/test_commandline.py +++ b/tests/test_commandline.py @@ -273,4 +273,5 @@ def test_hanging_message_for_tool_running(self): check_job_hanging(self.job) self.job.refresh_from_db() self.assertIsNotNone(self.job.message["alerts"][0]) - self.assertTrue(command_log_path in self.job.message["alerts"][0]["message"]) + if command_log_path: + self.assertTrue(command_log_path in self.job.message["alerts"][0]["message"]) From 897ee0331309486606be13689c57ae4c3e265d20 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 2 Apr 2025 09:41:46 -0400 Subject: [PATCH 064/235] Remove migration to merge with develop --- .../migrations/0019_auto_20250401_1659.py | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 orchestrator/migrations/0019_auto_20250401_1659.py diff --git a/orchestrator/migrations/0019_auto_20250401_1659.py b/orchestrator/migrations/0019_auto_20250401_1659.py deleted file mode 100644 index ef916148..00000000 --- a/orchestrator/migrations/0019_auto_20250401_1659.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.28 on 2025-04-01 16:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("orchestrator", "0018_auto_20241114_1618"), - ] - - operations = [ - migrations.AlterField( - model_name="commandlinetooljob", - name="job_id", - field=models.CharField(max_length=50), - ), - ] From adc82245d846078f9a557816572df0f9964fc39f Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 2 Apr 2025 09:42:57 -0400 Subject: [PATCH 065/235] Added extra migration --- .../migrations/0020_auto_20250402_0942.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 orchestrator/migrations/0020_auto_20250402_0942.py diff --git a/orchestrator/migrations/0020_auto_20250402_0942.py b/orchestrator/migrations/0020_auto_20250402_0942.py new file mode 100644 index 00000000..58f9f1e9 --- /dev/null +++ b/orchestrator/migrations/0020_auto_20250402_0942.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2025-04-02 09:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orchestrator', '0019_job_log_prefix'), + ] + + operations = [ + migrations.AlterField( + model_name='commandlinetooljob', + name='job_id', + field=models.CharField(max_length=50), + ), + ] From e2a83be9dc5f7064cf5dad30b6dbee17d3a6da1a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 2 Apr 2025 09:43:15 -0400 Subject: [PATCH 066/235] Added black formatting --- orchestrator/migrations/0020_auto_20250402_0942.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/orchestrator/migrations/0020_auto_20250402_0942.py b/orchestrator/migrations/0020_auto_20250402_0942.py index 58f9f1e9..9243e6b0 100644 --- a/orchestrator/migrations/0020_auto_20250402_0942.py +++ b/orchestrator/migrations/0020_auto_20250402_0942.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('orchestrator', '0019_job_log_prefix'), + ("orchestrator", "0019_job_log_prefix"), ] operations = [ migrations.AlterField( - model_name='commandlinetooljob', - name='job_id', + model_name="commandlinetooljob", + name="job_id", field=models.CharField(max_length=50), ), ] From 2bf96b8487aaf741c20314f686ea69b5c0748599 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 2 Apr 2025 09:45:04 -0400 Subject: [PATCH 067/235] Update test to work with missing tool log from tracking --- tests/test_commandline.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_commandline.py b/tests/test_commandline.py index 706af674..70b5dea8 100644 --- a/tests/test_commandline.py +++ b/tests/test_commandline.py @@ -267,11 +267,11 @@ def test_hanging_message_for_tool_running(self): single_job.save() first_command = CommandLineToolJob.objects.first() first_command.status = Status.RUNNING + example_log = "path/to/log.log" + first_command.details["log_path"] = example_log first_command.save() - command_log_path = first_command.details["log_path"] with override_settings(MAX_HANGING_HOURS=0): check_job_hanging(self.job) self.job.refresh_from_db() self.assertIsNotNone(self.job.message["alerts"][0]) - if command_log_path: - self.assertTrue(command_log_path in self.job.message["alerts"][0]["message"]) + self.assertTrue(example_log in self.job.message["alerts"][0]["message"]) From 4a8143c7ad706f15c701a069b8ba0357c9486eaa Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 2 Apr 2025 09:46:49 -0400 Subject: [PATCH 068/235] Bump version --- ridgeback/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/__init__.py b/ridgeback/__init__.py index 9483ec48..4ee5c8be 100644 --- a/ridgeback/__init__.py +++ b/ridgeback/__init__.py @@ -1 +1 @@ -__version__ = "1.37.0" +__version__ = "1.38.0" From f0010fb00f0134c713cd7bd5e5e54b4fec3b6f1b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 2 Apr 2025 10:45:46 -0400 Subject: [PATCH 069/235] Fix env typo --- compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index c6886c75..d134e541 100644 --- a/compose.yaml +++ b/compose.yaml @@ -58,7 +58,7 @@ services: python3 ${RIDGEBACK_PATH}/manage.py wait_for_db && python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput && python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput && - python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_POR}T >> /ridgeback_compose/server/boot.log 2>&1" + python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_PORT} >> /ridgeback_compose/server/boot.log 2>&1" healthcheck: test: ["CMD-SHELL", "curl --fail http://localhost:${RIDGEBACK_PORT}/ || exit 1"] interval: 60s From 5ac07ff773829882a415099f9b0975c358230cd9 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 9 Apr 2025 12:38:15 -0400 Subject: [PATCH 070/235] Update compose.yaml file - split celery tasks - Add slurm support - Update postgres config - Update folder creation on host --- compose.yaml | 488 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 360 insertions(+), 128 deletions(-) diff --git a/compose.yaml b/compose.yaml index c6886c75..2296d3a1 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,191 +1,423 @@ name: 'Ridgeback Services' +x-ridgeback_celery: + &ridgeback_celery + image: mskcc/ridgeback:${RIDGEBACK_VERSION} + restart: always + user: "${DOCKER_UID}:${DOCKER_GID}" + env_file: .env + environment: + - RIDGEBACK_DB_URL=ridgeback_postgres + - RIDGEBACK_MEMCACHED_HOST=ridgeback_memcached + - RIDGEBACK_RABBITMQ_URL=ridgeback_rabbitmq + - RIDGEBACK_DB_PORT=5432 + - RIDGEBACK_MEMCACHED_PORT=11211 + - RIDGEBACK_RABBITMQ_PORT=5672 + - RIDGEBACK_LOG_PATH=/ridgeback/celery/logs/django_server.log + - PATH=${PATH}:/usr/batchsystem/bin + volumes: + - ${SLURM_BIN_PATH}:/usr/batchsystem/bin + - ./logs/:/ridgeback/celery/logs/ + - ./celery/:/ridgeback/celery/ + - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} + - ${CLUSTER_SCRATCH_MOUNT}:${CLUSTER_SCRATCH_MOUNT} + - type: bind + source: ${SLURM_ETC} + target: ${SLURM_ETC} + read_only: true + - type: bind + source: ${SLURM_LIB_PATH} + target: ${SLURM_LIB_PATH} + read_only: true + - type: bind + source: ${SLURM_ETC_PASSWD} + target: ${SLURM_ETC_PASSWD} + read_only: true + - type: bind + source: ${SLURM_MUNGE_VAR} + target: ${SLURM_MUNGE_VAR} + read_only: true + - type: bind + source: ${SLURM_LIBMUNGE_OBJECT} + target: ${SLURM_LIBMUNGE_OBJECT} + read_only: true + - type: bind + source: /admin + target: /admin + read_only: true + entrypoint: ["/bin/bash","-c"] + post_start: + - command: groupadd -f -g ${DOCKER_GID} ridgeback_user + user: root + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status || exit 1" + interval: 30s + timeout: 3s + retries: 3 + depends_on: + ridgeback_postgres: + condition: service_healthy + restart: true + ridgeback_memcached: + condition: service_healthy + restart: false + ridgeback_rabbitmq: + condition: service_healthy + restart: false + ridgeback_celery_beat: + condition: service_healthy + restart: false + services: - postgres: + ridgeback_create_volumes: + image: alpine:3.8 + restart: no + volumes: + - ./postgres:/postgres + - ./logs:/logs + - ./celery:/celery + - ./rabbitmq:/rabbitmq + - ./server/:/server + - ./logrotate/:/logrotate + - ${DB_BACKUP_PATH}:/db_backup + entrypoint: ["/bin/sh", "-c"] + command: + - | + chown -R ${DOCKER_UID}:${DOCKER_GID} /postgres + chown -R ${DOCKER_UID}:${DOCKER_GID} /logs + chown -R ${DOCKER_UID}:${DOCKER_GID} /celery + chown -R ${DOCKER_UID}:${DOCKER_GID} /rabbitmq + chown -R ${DOCKER_UID}:${DOCKER_GID} /server + chown -R ${DOCKER_UID}:${DOCKER_GID} /logrotate + chown -R ${DOCKER_UID}:${DOCKER_GID} /db_backup + ridgeback_postgres: image: postgres:17 restart: on-failure + user: "${DOCKER_UID}:${DOCKER_GID}" volumes: - - ./ridgeback_compose/database:/var/lib/postgresql/data/ + - ./postgres/:/var/lib/postgresql/data/ environment: - POSTGRES_USER=${RIDGEBACK_DB_USERNAME} - POSTGRES_PASSWORD=${RIDGEBACK_DB_PASSWORD} - POSTGRES_DB=${RIDGEBACK_DB_NAME} ports: - ${RIDGEBACK_DB_PORT}:5432 + command: + - -c + - max_connections=300 + - -c + - shared_buffers=15GB + - -c + - effective_cache_size=45GB + - -c + - maintenance_work_mem=2GB + - -c + - checkpoint_completion_target=0.9 + - -c + - wal_buffers=16MB + - -c + - default_statistics_target=100 + - -c + - random_page_cost=1.1 + - -c + - effective_io_concurrency=200 + - -c + - work_mem=13107kB + - -c + - huge_pages=try + - -c + - min_wal_size=2GB + - -c + - max_wal_size=8GB + - -c + - max_worker_processes=20 + - -c + - max_parallel_workers_per_gather=4 + - -c + - max_parallel_workers=20 + - -c + - max_parallel_maintenance_workers=4 healthcheck: test: ["CMD-SHELL", "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'"] - interval: 60s + interval: 30s timeout: 3s retries: 3 - memcached: - image: memcached:1.6.36 + depends_on: + - ridgeback_create_volumes + ridgeback_memcached: + image: bitnami/memcached:1.6.37 restart: on-failure - ports: - - ${RIDGEBACK_MEMCACHED_PORT}:11211 + user: "${DOCKER_UID}:${DOCKER_GID}" + expose: + - "11211" healthcheck: test: ["CMD", "nc", "-z", "localhost", "11211"] - interval: 10s + interval: 30s timeout: 5s retries: 3 - rabbitmq: + ridgeback_rabbitmq: image: rabbitmq:4.0.6-management-alpine restart: always + user: "${DOCKER_UID}:${DOCKER_GID}" volumes: - - ./ridgeback_compose/rabbitmq/data/:/var/lib/rabbitmq/ - - ./ridgeback_compose/rabbitmq/log/:/var/log/rabbitmq/ + - ./rabbitmq/:/var/lib/rabbitmq/ + - ./logs/:/var/log/rabbitmq/ ports: - - ${RIDGEBACK_RABBITMQ_PORT}:5672 - ${RIDGEBACK_RABBITMQ_MANAGEMENT_PORT}:15672 + expose: + - "5672" environment: - RABBITMQ_NODENAME=rabbitmq_ridgeback - - RABBITMQ_NODE_IP_ADDRESS=127.0.0.1 - RABBITMQ_DEFAULT_USER=${RIDGEBACK_RABBITMQ_USERNAME} - RABBITMQ_DEFAULT_PASS=${RIDGEBACK_RABBITMQ_PASSWORD} + - RABBITMQ_LOGS=/var/log/rabbitmq/rabbitmq.log healthcheck: test: ["CMD", "rabbitmq-diagnostics", "check_running"] - interval: 60s + interval: 30s timeout: 3s retries: 3 + depends_on: + - ridgeback_create_volumes ridgeback_webserver: - iamge: mskcc/ridgeback:${RIDGEBACK_VERSION} + image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always + user: "${DOCKER_UID}:${DOCKER_GID}" + env_file: .env + environment: + - RIDGEBACK_DB_URL=ridgeback_postgres + - RIDGEBACK_MEMCACHED_HOST=ridgeback_memcached + - RIDGEBACK_RABBITMQ_URL=ridgeback_rabbitmq + - RIDGEBACK_DB_PORT=5432 + - RIDGEBACK_MEMCACHED_PORT=11211 + - RIDGEBACK_RABBITMQ_PORT=5672 + - RIDGEBACK_LOG_PATH=/ridgeback/server/django_server.log volumes: - - ./ridgeback_compose/server:/ridgeback/server + - ./logs/:/ridgeback/server/ + - ./server/:/ridgeback_staticfiles/ ports: - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} - command: > - bash -c " - python3 ${RIDGEBACK_PATH}/manage.py wait_for_db && - python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput && - python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput && - python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_POR}T >> /ridgeback_compose/server/boot.log 2>&1" + entrypoint: ["/bin/bash","-c"] + command: + - | + python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput + echo "User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin','voyager@mskcc.org','${RIDGEBACK_DB_PASSWORD}')" | python3 ${RIDGEBACK_PATH}/manage.py shell_plus + python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput + python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_PORT} >> /ridgeback/server/web_server.log 2>&1 healthcheck: - test: ["CMD-SHELL", "curl --fail http://localhost:${RIDGEBACK_PORT}/ || exit 1"] - interval: 60s + test: ["CMD-SHELL", "curl -sSf http://localhost:${RIDGEBACK_PORT}/ || exit 1"] + interval: 30s timeout: 3s retries: 3 depends_on: - postgres: + ridgeback_postgres: condition: service_healthy restart: true - memcached: + ridgeback_memcached: condition: service_healthy restart: false - rabbitmq: + ridgeback_rabbitmq: condition: service_healthy restart: false - ridgeback_celery: - iamge: mskcc/ridgeback:${RIDGEBACK_VERSION} - restart: always - user: "${DOCKER_UID}:${DOCKER_GID}" - volumes: - - ${SLURM_BIN_PATH}:/usr/batchsystem/bin - - ${SLURM_ETC}:${SLURM_ETC} - - ${SLURM_LIB_PATH}:${SLURM_LIB_PATH} - - ${SLURM_ETC_PASSWD}:${SLURM_ETC_PASSWD} - - ${SLURM_MUNGE_VAR}:${SLURM_MUNGE_VAR} - - ${SLURM_LIBMUNGE_OBJECT}:${SLURM_LIBMUNGE_OBJECT} - - ./ridgeback_compose/celery/logs:/ridgeback/celery/logs - - ./ridgeback_compose/celery/pids:/ridgeback/celery/pids - - ./ridgeback_compose/celery:/ridgeback/celery - - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - command: > - bash -c "source ${RIDGEBACK_VENV}/bin/activate && - pip3 install --upgrade pip && - pip3 install --force-reinstall 'setuptools<58.0.0 && - pip3 install -r /usr/bin/ridgeback/requirements.txt && - pip3 install -r requirements-toil.txt && - - echo 'Running orchestrator beat...' && - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator beat \ - --detach \ - -l info \ - -f /ridgeback/celery/logs/ridgeback_beat.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid \ - -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule -detach && + ridgeback_celery_beat: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate + if ! command -v toil-cwl-runner 2>&1 >/dev/null + then + pip3 install --upgrade pip && + pip3 install --force-reinstall 'setuptools<58.0.0' && + pip3 install "cython<3.0.0" wheel && + pip3 install "pyyaml==5.4.1" --no-build-isolation && + pip3 install -r ${RIDGEBACK_PATH}/requirements.txt && + pip3 install -r ${RIDGEBACK_PATH}/requirements-toil.txt + fi + + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid - echo 'Running command queue worker...' && + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running orchestrator beat...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator beat \ + -l info \ + -f /ridgeback/celery/logs/ridgeback_beat.log \ + --pidfile $$PIDFILE \ + -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule + healthcheck: + test: ["CMD-SHELL", "ps -p $(pgrep -F /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid)"] + interval: 30s + timeout: 3s + retries: 3 + depends_on: + ridgeback_postgres: + condition: service_healthy + restart: true + ridgeback_memcached: + condition: service_healthy + restart: false + ridgeback_rabbitmq: + condition: service_healthy + restart: false + ridgeback_celery_command_queue: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - --detach \ - -l info \ - -Q ${RIDGEBACK_COMMAND_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid \ - --concurrency=30 \ - -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} && + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running command queue worker...' - echo 'Running action queue worker...'' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - --detach \ - -l info \ - -Q ${RIDGEBACK_ACTION_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid \ - --concurrency=10 \ - -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} && + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_COMMAND_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=30 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} + ridgeback_celery_action_queue: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate - echo 'Running check status queue worker...'' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - --detach \ - -l info \ - -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid \ - --concurrency=1 && + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running action queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_ACTION_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} + ridgeback_celery_check_status_queue: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate - echo 'Running submit job queue worker...' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - --detach \ - -l info \ - -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid \ - --concurrency=1 && + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running check status queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 + ridgeback_celery_submit_job_queue: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate - echo 'Running short queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running submit job queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=5 + ridgeback_celery_set_permission_queue: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - --detach \ - -l info \ - -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid \ - --concurrency=10 && + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running set permission queue worker...' - echo 'Running cleanup queue worker...' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - --detach \ - -l info \ - -Q ${RIDGEBACK_CLEANUP_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ - --pidfile /ridgeback/celery/pids/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid \ - --concurrency=2" - healthcheck: - test: ["CMD-SHELL", "sh -c 'source ${RIDGEBACK_VENV}/bin/activate; celery -A orchestrator' status || exit 1"] - interval: 60s - timeout: 3s - retries: 3 + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 + ridgeback_celery_cleanup_queue: + <<: *ridgeback_celery + command: + - | + source ${RIDGEBACK_VENV}/bin/activate + + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running cleanup queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_CLEANUP_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=2 + ridgeback_logrotate: + image: mskcc/voyager-compose-utils:1.0.0 + restart: always + user: "${DOCKER_UID}:${DOCKER_GID}" + volumes: + - ./logs:/logs + - ./logrotate/:/logrotate + entrypoint: ["/bin/sh", "-c"] + command: + - | + mkdir -p /logs/archive + cat > /logrotate/logrotate.conf < /logrotate/logrotate.cron <> /logs/logrotate_cron.log 2>&1 depends_on: - postgres: + ridgeback_celery_beat: condition: service_healthy - restart: true - memcached: + restart: false + ridgeback_celery_command_queue: condition: service_healthy restart: false - rabbitmq: + ridgeback_db_backup: + image: mskcc/voyager-compose-utils:1.0.0 + restart: always + user: "${DOCKER_UID}:${DOCKER_GID}" + volumes: + - ./logs:/logs + - ${DB_BACKUP_PATH}:/db_backup + environment: + - PGPASSWORD=${RIDGEBACK_DB_USERNAME} + entrypoint: ["/bin/sh", "-c"] + command: + - | + mkdir -p /db_backup/archive + cat > /db_backup/db_backup.cron <> /logs/db_backup_cron.log 2>&1 + depends_on: + ridgeback_celery_beat: condition: service_healthy restart: false - + ridgeback_celery_command_queue: + condition: service_healthy + restart: false \ No newline at end of file From ee26c0810291b085a5e793da7d3a70ebf3462ff4 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 29 Apr 2025 16:31:59 -0400 Subject: [PATCH 071/235] Add install updates for Dockerfile --- Dockerfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Dockerfile b/Dockerfile index 8868c3c9..f338311c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,12 @@ RUN apt-get update \ && cd /usr/bin \ && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ && cd /usr/bin/ridgeback \ + # Install alternative ssl library + && wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ + && dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ + # Install libffi6 for TOIL + && wget http://archive.ubuntu.com/ubuntu/pool/main/libf/libffi/libffi6_3.2.1-8_amd64.deb \ + && dpkg -i libffi6_3.2.1-8_amd64.deb \ # Install python packages && pip3 install --upgrade pip \ && pip3 install python-ldap \ From 16a36508b8ca231527163d7a0cf75dba3a20c3f9 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 29 Apr 2025 17:45:04 -0400 Subject: [PATCH 072/235] Update slurm walltime arg --- batch_systems/slurm_client/slurm_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 47bbfb2d..a1cdf704 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -96,7 +96,7 @@ def terminate(self, job_id): def set_walltime(self, expected_limit, hard_limit): walltime_args = [] if expected_limit: - walltime_args = walltime_args + [f"--t={expected_limit}"] + walltime_args = walltime_args + [f"--time={expected_limit}"] if hard_limit: self.logger.debug( "Hard limits on submit are no supported, please check the cluster KillWait and OverTimeLimit params" From d62df4f32ce8ffc4d128f6fd527a09934ed0f0b3 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Apr 2025 15:28:14 -0400 Subject: [PATCH 073/235] Set memlimit properly in Slurm --- batch_systems/slurm_client/slurm_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index a1cdf704..5165c420 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -106,9 +106,9 @@ def set_walltime(self, expected_limit, hard_limit): def set_memlimit(self, mem_limit, default=None): mem_limit_args = [] if default: - mem_limit = [f"--mem={default}"] + mem_limit = [f"--mem={default}G"] if mem_limit: - mem_limit_args = [f"--mem={mem_limit}"] + mem_limit_args = [f"--mem={mem_limit}G"] return mem_limit_args def set_group(self, group_id): From aa9762b8ecc404bc6d368f1da9a7798fb9ce7d98 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Apr 2025 17:51:21 -0400 Subject: [PATCH 074/235] Formatting fixes --- compose.yaml | 224 +++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 116 deletions(-) diff --git a/compose.yaml b/compose.yaml index cf2358b1..533c45a3 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,7 +1,8 @@ -name: "Ridgeback Services" +name: 'Ridgeback Services' -x-ridgeback_celery: &ridgeback_celery - image: mskcc/ridgeback:${RIDGEBACK_VERSION} +x-ridgeback_celery: + &ridgeback_celery + image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" env_file: .env @@ -81,7 +82,7 @@ services: - ${DB_BACKUP_PATH}:/db_backup entrypoint: ["/bin/sh", "-c"] command: - - | + - | chown -R ${DOCKER_UID}:${DOCKER_GID} /postgres chown -R ${DOCKER_UID}:${DOCKER_GID} /logs chown -R ${DOCKER_UID}:${DOCKER_GID} /celery @@ -137,11 +138,7 @@ services: - -c - max_parallel_maintenance_workers=4 healthcheck: - test: - [ - "CMD-SHELL", - "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'", - ] + test: ["CMD-SHELL", "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'"] interval: 30s timeout: 3s retries: 3 @@ -182,7 +179,7 @@ services: depends_on: - ridgeback_create_volumes ridgeback_webserver: - image: mskcc/ridgeback:${RIDGEBACK_VERSION} + image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" env_file: .env @@ -199,16 +196,15 @@ services: - ./server/:/ridgeback_staticfiles/ ports: - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} - entrypoint: ["/bin/bash", "-c"] + entrypoint: ["/bin/bash","-c"] command: - | - python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput - echo "User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin','voyager@mskcc.org','${RIDGEBACK_DB_PASSWORD}')" | python3 ${RIDGEBACK_PATH}/manage.py shell_plus - python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput - python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_PORT} >> /ridgeback/server/web_server.log 2>&1 + python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput + echo "User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin','voyager@mskcc.org','${RIDGEBACK_DB_PASSWORD}')" | python3 ${RIDGEBACK_PATH}/manage.py shell_plus + python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput + python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_PORT} >> /ridgeback/server/web_server.log 2>&1 healthcheck: - test: - ["CMD-SHELL", "curl -sSf http://localhost:${RIDGEBACK_PORT}/ || exit 1"] + test: ["CMD-SHELL", "curl -sSf http://localhost:${RIDGEBACK_PORT}/ || exit 1"] interval: 30s timeout: 3s retries: 3 @@ -226,34 +222,29 @@ services: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate - if ! command -v toil-cwl-runner 2>&1 >/dev/null - then - pip3 install --upgrade pip && - pip3 install --force-reinstall 'setuptools<58.0.0' && - pip3 install "cython<3.0.0" wheel && - pip3 install "pyyaml==5.4.1" --no-build-isolation && - pip3 install -r ${RIDGEBACK_PATH}/requirements.txt && - pip3 install -r ${RIDGEBACK_PATH}/requirements-toil.txt - fi - - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid + if ! python3 -c "import toil" 2>&1 >/dev/null + then + pip3 install --upgrade pip && + pip3 install --force-reinstall 'setuptools<58.0.0' && + pip3 install "cython<3.0.0" wheel && + pip3 install "pyyaml==5.4.1" --no-build-isolation && + pip3 install -r ${RIDGEBACK_PATH}/requirements.txt && + pip3 install -r ${RIDGEBACK_PATH}/requirements-toil.txt + fi + + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running orchestrator beat...' + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running orchestrator beat...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator beat \ - -l info \ - -f /ridgeback/celery/logs/ridgeback_beat.log \ - --pidfile $$PIDFILE \ - -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator beat \ + -l info \ + -f /ridgeback/celery/logs/ridgeback_beat.log \ + --pidfile $$PIDFILE \ + -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule healthcheck: - test: - [ - "CMD-SHELL", - "ps -p $(pgrep -F /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid)", - ] + test: ["CMD-SHELL", "ps -p $(pgrep -F /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid)"] interval: 30s timeout: 3s retries: 3 @@ -271,116 +262,117 @@ services: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running command queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running command queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_COMMAND_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=30 \ - -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_COMMAND_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=30 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} ridgeback_celery_action_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running action queue worker...' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_ACTION_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=10 \ - -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running action queue worker...' + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_ACTION_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} ridgeback_celery_check_status_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate - - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running check status queue worker...' + source ${RIDGEBACK_VENV}/bin/activate - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=10 + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running check status queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 ridgeback_celery_submit_job_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running submit job queue worker...' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=5 + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running submit job queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=5 ridgeback_celery_set_permission_queue: <<: *ridgeback_celery command: - - | - source ${RIDGEBACK_VENV}/bin/activate + - | + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running set permission queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running set permission queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=10 + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 ridgeback_celery_cleanup_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running cleanup queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running cleanup queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_CLEANUP_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=2 + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_CLEANUP_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=2 ridgeback_logrotate: image: mskcc/voyager-compose-utils:1.0.0 restart: always user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net volumes: - ./logs:/logs - ./logrotate/:/logrotate entrypoint: ["/bin/sh", "-c"] command: - - | + - | mkdir -p /logs/archive cat > /logrotate/logrotate.conf < /db_backup/db_backup.cron < Date: Wed, 30 Apr 2025 17:52:51 -0400 Subject: [PATCH 075/235] Add network to compose --- compose.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compose.yaml b/compose.yaml index 533c45a3..e21503d0 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,6 +5,8 @@ x-ridgeback_celery: image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net env_file: .env environment: - RIDGEBACK_DB_URL=ridgeback_postgres @@ -94,6 +96,8 @@ services: image: postgres:17 restart: on-failure user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net volumes: - ./postgres/:/var/lib/postgresql/data/ environment: @@ -148,6 +152,8 @@ services: image: bitnami/memcached:1.6.37 restart: on-failure user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net expose: - "11211" healthcheck: @@ -159,6 +165,8 @@ services: image: rabbitmq:4.0.6-management-alpine restart: always user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net volumes: - ./rabbitmq/:/var/lib/rabbitmq/ - ./logs/:/var/log/rabbitmq/ @@ -182,6 +190,8 @@ services: image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net env_file: .env environment: - RIDGEBACK_DB_URL=ridgeback_postgres @@ -401,6 +411,8 @@ services: image: mskcc/voyager-compose-utils:1.0.0 restart: always user: "${DOCKER_UID}:${DOCKER_GID}" + networks: + - voyager_net volumes: - ./logs:/logs - ${DB_BACKUP_PATH}:/db_backup @@ -421,3 +433,7 @@ services: ridgeback_celery_command_queue: condition: service_healthy restart: false +networks: + voyager_net: + name: voyager_network + driver: bridge \ No newline at end of file From 0c3be54d906253273f75a5b9d4316aff24946176 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Apr 2025 17:53:28 -0400 Subject: [PATCH 076/235] Configure file and group permissions --- compose.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compose.yaml b/compose.yaml index e21503d0..2f6e88b6 100644 --- a/compose.yaml +++ b/compose.yaml @@ -7,6 +7,10 @@ x-ridgeback_celery: user: "${DOCKER_UID}:${DOCKER_GID}" networks: - voyager_net + group_add: + - ${DOCKER_GID} + - ${GID_FILE_1} + - ${GID_FILE_2} env_file: .env environment: - RIDGEBACK_DB_URL=ridgeback_postgres @@ -44,13 +48,10 @@ x-ridgeback_celery: target: ${SLURM_LIBMUNGE_OBJECT} read_only: true - type: bind - source: /admin - target: /admin + source: ${CLUSTER_ADMIN_MOUNT} + target: ${CLUSTER_ADMIN_MOUNT} read_only: true - entrypoint: ["/bin/bash", "-c"] - post_start: - - command: groupadd -f -g ${DOCKER_GID} ridgeback_user - user: root + entrypoint: ["/bin/bash","-c"] healthcheck: test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status || exit 1" interval: 30s From d62cec0e0e172818d247819011a14b1ebc6bc281 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 1 May 2025 20:05:54 -0400 Subject: [PATCH 077/235] Remove memlimit which should not be in tool args --- submitter/toil_submitter/toil_jobsubmitter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 0b63d658..76500fb4 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -199,7 +199,6 @@ def _tool_args(self): expected_limit = max(1, int(self.tool_walltime / 3)) hard_limit = self.tool_walltime args = self.batch_system.set_walltime(expected_limit, hard_limit) - args.extend(self._memlimit()) return args def _service_queue(self): From ea033d6561e58b7561a0938ec474a0b5ab0ab2ec Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 1 May 2025 20:07:59 -0400 Subject: [PATCH 078/235] Add compose update --- compose.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index 2f6e88b6..f66b235a 100644 --- a/compose.yaml +++ b/compose.yaml @@ -27,6 +27,7 @@ x-ridgeback_celery: - ./celery/:/ridgeback/celery/ - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - ${CLUSTER_SCRATCH_MOUNT}:${CLUSTER_SCRATCH_MOUNT} + - ${CLUSTER_FILESYSTEM_SHARE_MOUNT}:${CLUSTER_FILESYSTEM_SHARE_MOUNT} - type: bind source: ${SLURM_ETC} target: ${SLURM_ETC} @@ -187,7 +188,7 @@ services: retries: 3 depends_on: - ridgeback_create_volumes - ridgeback_webserver: + ridgeback: image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" From cd2c00ba26e01442b837fe4e4291b4abf4a3a22c Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 2 May 2025 13:22:57 -0400 Subject: [PATCH 079/235] Add tests for SLUM and updated tests --- batch_systems/batch_system.py | 2 +- batch_systems/slurm_client/slurm_client.py | 6 +- orchestrator/models.py | 4 +- orchestrator/tasks.py | 25 +- tests/test_tasks.py | 643 ++++++++++++++------- 5 files changed, 459 insertions(+), 221 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index 6f46b9d2..cb90bf9f 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -33,7 +33,7 @@ def __init__(self): def submit(self, command, job_args, stdout, job_id, env={}): """ - Submit command to LSF and store log in stdout + Submit command to bath system and store log in stdout Args: command (list): command to submit diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 5165c420..b4f36cd9 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -96,11 +96,11 @@ def terminate(self, job_id): def set_walltime(self, expected_limit, hard_limit): walltime_args = [] if expected_limit: - walltime_args = walltime_args + [f"--time={expected_limit}"] - if hard_limit: self.logger.debug( - "Hard limits on submit are no supported, please check the cluster KillWait and OverTimeLimit params" + "Expected limits on submit are no supported, please check the cluster KillWait and OverTimeLimit params" ) + if hard_limit: + walltime_args = [f"--time={hard_limit}"] return walltime_args def set_memlimit(self, mem_limit, default=None): diff --git a/orchestrator/models.py b/orchestrator/models.py index cd3b961a..9e7b0cd7 100755 --- a/orchestrator/models.py +++ b/orchestrator/models.py @@ -193,13 +193,15 @@ class Job(BaseModel): metadata = JSONField(blank=True, null=True, default=dict) def job_prepared(self, job_store_dir, job_work_dir, job_output_dir, log_path, log_prefix): + from batch_systems.batch_system import get_batch_system + self.status = Status.PREPARED self.job_store_location = job_store_dir self.working_dir = job_work_dir self.output_directory = job_output_dir self.log_dir = log_path self.log_prefix = log_prefix - self.message["log"] = os.path.join(job_work_dir, "lsf.log") + self.message["log"] = os.path.join(job_work_dir, get_batch_system().logfileName) self.save( update_fields=[ "status", diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 8913eb2a..dfc769a5 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -22,7 +22,6 @@ logger = logging.getLogger(__name__) -BATCH_SYSTEM = get_batch_system() def get_job_info_path(job_id): @@ -55,7 +54,7 @@ def save_job_info(job_id, external_id, job_store_location, working_dir, output_d def suspend_job(job): if Status(job.status).transition(Status.SUSPENDED): - job_suspended = BATCH_SYSTEM.suspend(str(job.id)) + job_suspended = get_batch_system().suspend(str(job.id)) if not job_suspended: raise RetryException("Failed to suspend job: %s" % str(job.id)) job.update_status(Status.SUSPENDED) @@ -75,7 +74,7 @@ def resume_job(job): log_prefix=job.log_prefix, app_name=job.metadata["pipeline_name"], ) - job_resumed = BATCH_SYSTEM.resume(submitter.job_id) + job_resumed = get_batch_system().resume(submitter.job_id) if not job_resumed: raise RetryException("Failed to resume job: %s" % str(job.id)) job.update_status(Status.RUNNING) @@ -215,7 +214,7 @@ def prepare_job(job): def submit_job_to_batch_system(job, retries=0): if Status(job.status).transition(Status.SUBMITTED): - logger.info(f"Submitting job {str(job.id)} to {BATCH_SYSTEM.name}. Try {retries}") + logger.info(f"Submitting job {str(job.id)} to {get_batch_system().name}. Try {retries}") submitter = JobSubmitterFactory.factory( job.type, str(job.id), @@ -232,7 +231,7 @@ def submit_job_to_batch_system(job, retries=0): ) try: command_line, args, log_path, job_id, env = submitter.get_submit_command() - external_job_id = BATCH_SYSTEM.submit(command_line, args, log_path, job_id, env) + external_job_id = get_batch_system().submit(command_line, args, log_path, job_id, env) except Exception as f: if retries < 5: logger.exception(str(f)) @@ -241,7 +240,7 @@ def submit_job_to_batch_system(job, retries=0): logger.exception(str(f)) raise StopException(f"Failed to submit job to scheduler {str(job.id)} no more retries") else: - logger.info(f"Job {str(job.id)} submitted to {BATCH_SYSTEM.name} with id: {external_job_id}") + logger.info(f"Job {str(job.id)} submitted to {get_batch_system().name} with id: {external_job_id}") job.submitted_to_scheduler(external_job_id) # Keeping this for debugging purposes save_job_info( @@ -305,7 +304,7 @@ def check_job_status(job): ): return try: - batch_system_status, batch_system_message = BATCH_SYSTEM.status(str(job.external_id)) + batch_system_status, batch_system_message = get_batch_system().status(str(job.external_id)) except FetchStatusException as e: # If failed to check status on batch system retry logger.exception(e) @@ -466,7 +465,7 @@ def terminate_job(job): Status.SUSPENDED, Status.UNKNOWN, ): - job_killed = BATCH_SYSTEM.terminate(str(job.id)) + job_killed = get_batch_system().terminate(str(job.id)) if not job_killed: raise RetryException("Failed to TERMINATE job %s" % str(job.id)) job.terminate() @@ -523,17 +522,21 @@ def full_cleanup_jobs(self): @shared_task(bind=True) def cleanup_completed_jobs(self): - cleanup_jobs(Status.COMPLETED, settings.CLEANUP_COMPLETED_JOBS, exclude=["input.json", BATCH_SYSTEM.logfileName]) + cleanup_jobs( + Status.COMPLETED, settings.CLEANUP_COMPLETED_JOBS, exclude=["input.json", get_batch_system().logfileName] + ) @shared_task(bind=True) def cleanup_failed_jobs(self): - cleanup_jobs(Status.FAILED, settings.CLEANUP_FAILED_JOBS, exclude=["input.json", BATCH_SYSTEM.logfileName]) + cleanup_jobs(Status.FAILED, settings.CLEANUP_FAILED_JOBS, exclude=["input.json", get_batch_system().logfileName]) @shared_task(bind=True) def cleanup_terminated_jobs(self): - cleanup_jobs(Status.TERMINATED, settings.CLEANUP_TERMINATED_JOBS, exclude=["input.json", BATCH_SYSTEM.logfileName]) + cleanup_jobs( + Status.TERMINATED, settings.CLEANUP_TERMINATED_JOBS, exclude=["input.json", get_batch_system().logfileName] + ) def cleanup_jobs(status, time_delta, exclude=[]): diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 8c78ac2c..74f428e1 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -1,5 +1,5 @@ from unittest import skip -from django.test import TestCase +from django.test import TestCase, override_settings from orchestrator.models import Job, Status, PipelineType from orchestrator.tasks import ( prepare_job, @@ -13,7 +13,7 @@ from mock import patch, call import uuid from batch_systems.lsf_client.lsf_client import format_lsf_job_id - +from django.conf import settings from submitter.toil_submitter import ToilJobSubmitter MAX_RUNNING_JOBS = 3 @@ -31,21 +31,43 @@ def setUp(self): @patch("orchestrator.tasks.submit_job_to_batch_system") @patch("batch_systems.lsf_client.lsf_client.LSFClient.submit") @skip("Need to mock memcached lock") - def test_submit_polling(self, job_submitter, submit_job_to_batch_system, init): - init.return_value = None - job_submitter.return_value = ( - self.current_job.external_id, - self.current_job.job_store_location, - self.current_job.working_dir, - self.current_job.output_directory, - ) - submit_job_to_batch_system.return_value = None - created_jobs = len(Job.objects.filter(status=Status.CREATED)) - process_jobs() - self.assertEqual(submit_job_to_batch_system.delay.call_count, created_jobs) - submit_job_to_batch_system.reset_mock() - process_jobs() - self.assertEqual(submit_job_to_batch_system.delay.call_count, 0) + def test_submit_polling_lsf(self, job_submitter, submit_job_to_batch_system, init): + with override_settings(BATCH_SYSTEM="LSF"): + init.return_value = None + job_submitter.return_value = ( + self.current_job.external_id, + self.current_job.job_store_location, + self.current_job.working_dir, + self.current_job.output_directory, + ) + submit_job_to_batch_system.return_value = None + created_jobs = len(Job.objects.filter(status=Status.CREATED)) + process_jobs() + self.assertEqual(submit_job_to_batch_system.delay.call_count, created_jobs) + submit_job_to_batch_system.reset_mock() + process_jobs() + self.assertEqual(submit_job_to_batch_system.delay.call_count, 0) + + @patch("submitter.toil_submitter.toil_jobsubmitter.ToilJobSubmitter.__init__") + @patch("orchestrator.tasks.submit_job_to_batch_system") + @patch("batch_systems.slurm_client.slurm_client.SLURMClient.submit") + @skip("Need to mock memcached lock") + def test_submit_polling_slurm(self, job_submitter, submit_job_to_batch_system, init): + with override_settings(BATCH_SYSTEM="SLURM"): + init.return_value = None + job_submitter.return_value = ( + self.current_job.external_id, + self.current_job.job_store_location, + self.current_job.working_dir, + self.current_job.output_directory, + ) + submit_job_to_batch_system.return_value = None + created_jobs = len(Job.objects.filter(status=Status.CREATED)) + process_jobs() + self.assertEqual(submit_job_to_batch_system.delay.call_count, created_jobs) + submit_job_to_batch_system.reset_mock() + process_jobs() + self.assertEqual(submit_job_to_batch_system.delay.call_count, 0) @patch("submitter.toil_submitter.toil_jobsubmitter.ToilJobSubmitter.prepare_to_submit") def test_prepare_job(self, prepare_to_submit): @@ -63,216 +85,427 @@ def test_prepare_job(self, prepare_to_submit): @patch("batch_systems.lsf_client.lsf_client.LSFClient.submit") @patch("orchestrator.tasks.save_job_info") - def test_submit(self, save_job_info, submit): - save_job_info.return_value = None - submit.return_value = self.submitting_job.external_id - submit_job_to_batch_system(self.submitting_job) - self.submitting_job.refresh_from_db() - self.assertEqual(self.submitting_job.finished, None) - self.assertEqual(self.submitting_job.status, Status.SUBMITTED) - - def test_job_args(self): - job_id = str(uuid.uuid4()) - app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} - root_dir = "test_root" - resume_jobstore = None - walltime = None - tool_walltime = None - memlimit = None - inputs = {} - expected_job_group = "-g {}".format(format_lsf_job_id(job_id)) - jobsubmitterObject = ToilJobSubmitter( - job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit - ) - job_group = " ".join(jobsubmitterObject._job_group()) - self.assertEqual(job_group, expected_job_group) - - def test_job_args_walltime(self): - job_id = str(uuid.uuid4()) - app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} - root_dir = "test_root" - resume_jobstore = None - walltime = 7200 - tool_walltime = 24 - memlimit = None - inputs = {} - expected_job_args = "-W {}".format(walltime) - jobsubmitterObject = ToilJobSubmitter( - job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit - ) - leader_args_list = jobsubmitterObject._leader_args() - leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) - self.assertEqual(leader_args, expected_job_args) - - def test_job_args_tool_walltime(self): - job_id = str(uuid.uuid4()) - app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} - root_dir = "test_root" - resume_jobstore = None - walltime = 7200 - tool_walltime = 24 - walltime_hard = 24 - walltime_expected = 8 - memlimit = None - inputs = {} - expected_tool_args = "-We {} -W {}".format(walltime_expected, walltime_hard) - jobsubmitterObject = ToilJobSubmitter( - job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit - ) - tool_args_list = jobsubmitterObject._tool_args() - tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) - self.assertEqual(tool_args, expected_tool_args) - - def test_job_args_memlimit(self): - job_id = str(uuid.uuid4()) - app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} - root_dir = "test_root" - resume_jobstore = None - walltime = None - tool_walltime = None - memlimit = 10 - inputs = {} - expected_leader_args = "-M {}".format(memlimit) - jobsubmitterObject = ToilJobSubmitter( - job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit - ) - leader_args_list = jobsubmitterObject._leader_args() - leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) - self.assertEqual(leader_args, expected_leader_args) - - def test_job_args_all_options(self): - job_id = str(uuid.uuid4()) - app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} - root_dir = "test_root" - resume_jobstore = None - walltime = 7200 - tool_walltime = 24 - tool_walltime = 24 - walltime_hard = 24 - walltime_expected = 8 - memlimit = 10 - inputs = {} - expected_leader_args = "-W {} -M {}".format(walltime, memlimit) - expected_job_group = "-g {}".format(format_lsf_job_id(job_id)) - expected_tool_args = "-We {} -W {} -M {}".format(walltime_expected, walltime_hard, memlimit) - jobsubmitterObject = ToilJobSubmitter( - job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit - ) - leader_args_list = jobsubmitterObject._leader_args() - leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) - job_group = " ".join(jobsubmitterObject._job_group()) - tool_args_list = jobsubmitterObject._tool_args() - tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) - self.assertEqual(leader_args, expected_leader_args) - self.assertEqual(job_group, expected_job_group) - self.assertEqual(tool_args, expected_tool_args) + def test_submit_lsf(self, save_job_info, submit): + with override_settings(BATCH_SYSTEM="LSF"): + save_job_info.return_value = None + submit.return_value = self.submitting_job.external_id + submit_job_to_batch_system(self.submitting_job) + self.submitting_job.refresh_from_db() + self.assertEqual(self.submitting_job.finished, None) + self.assertEqual(self.submitting_job.status, Status.SUBMITTED) + + @patch("batch_systems.slurm_client.slurm_client.SLURMClient.submit") + @patch("orchestrator.tasks.save_job_info") + def test_submit_slurm(self, save_job_info, submit): + with override_settings(BATCH_SYSTEM="SLURM"): + save_job_info.return_value = None + submit.return_value = self.submitting_job.external_id + submit_job_to_batch_system(self.submitting_job) + self.submitting_job.refresh_from_db() + self.assertEqual(self.submitting_job.finished, None) + self.assertEqual(self.submitting_job.status, Status.SUBMITTED) + + def test_job_args_lsf(self): + with override_settings(BATCH_SYSTEM="LSF"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = None + tool_walltime = None + memlimit = None + inputs = {} + expected_job_group = "-g {}".format(format_lsf_job_id(job_id)) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + job_group = " ".join(jobsubmitterObject._job_group()) + self.assertEqual(job_group, expected_job_group) + + def test_job_args_slurm(self): + with override_settings(BATCH_SYSTEM="SLURM"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = None + tool_walltime = None + memlimit = None + inputs = {} + expected_job_group = "--wckey={}".format(job_id) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + job_group = " ".join(jobsubmitterObject._job_group()) + self.assertEqual(job_group, expected_job_group) + + def test_job_args_walltime_lsf(self): + with override_settings(BATCH_SYSTEM="LSF"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = 7200 + tool_walltime = 24 + memlimit = None + inputs = {} + expected_job_args = "-W {}".format(walltime) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + self.assertEqual(leader_args, expected_job_args) + + def test_job_args_walltime_slurm(self): + with override_settings(BATCH_SYSTEM="SLURM"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = 7200 + tool_walltime = 24 + memlimit = None + inputs = {} + expected_job_args = "--time={}".format(walltime) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + self.assertEqual(leader_args, expected_job_args) + + def test_job_args_tool_walltime_lsf(self): + with override_settings(BATCH_SYSTEM="LSF"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = 7200 + tool_walltime = 24 + walltime_hard = 24 + walltime_expected = 8 + memlimit = None + inputs = {} + expected_tool_args = "-We {} -W {}".format(walltime_expected, walltime_hard) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + tool_args_list = jobsubmitterObject._tool_args() + tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) + self.assertEqual(tool_args, expected_tool_args) + + def test_job_args_tool_walltime_slurm(self): + with override_settings(BATCH_SYSTEM="SLURM"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = 7200 + tool_walltime = 24 + walltime_hard = 24 + memlimit = None + inputs = {} + expected_tool_args = "--time={}".format(walltime_hard) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + tool_args_list = jobsubmitterObject._tool_args() + tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) + self.assertEqual(tool_args, expected_tool_args) + + def test_job_args_memlimit_lsf(self): + with override_settings(BATCH_SYSTEM="LSF"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = None + tool_walltime = None + memlimit = 10 + inputs = {} + expected_leader_args = "-M {}".format(memlimit) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + self.assertEqual(leader_args, expected_leader_args) + + def test_job_args_memlimit_slurm(self): + with override_settings(BATCH_SYSTEM="SLURM"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = None + tool_walltime = None + memlimit = 10 + inputs = {} + expected_leader_args = "--mem={}G".format(memlimit) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + self.assertEqual(leader_args, expected_leader_args) + + def test_job_args_all_options_lsf(self): + with override_settings(BATCH_SYSTEM="LSF"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = 7200 + tool_walltime = 24 + walltime_hard = 24 + walltime_expected = 8 + memlimit = 10 + inputs = {} + expected_leader_args = "-W {} -M {}".format(walltime, memlimit) + expected_job_group = "-g {}".format(format_lsf_job_id(job_id)) + expected_tool_args = "-We {} -W {}".format(walltime_expected, walltime_hard) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + job_group = " ".join(jobsubmitterObject._job_group()) + tool_args_list = jobsubmitterObject._tool_args() + tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) + self.assertEqual(leader_args, expected_leader_args) + self.assertEqual(job_group, expected_job_group) + self.assertEqual(tool_args, expected_tool_args) + + def test_job_args_all_options_slurm(self): + with override_settings(BATCH_SYSTEM="SLURM"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = 7200 + tool_walltime = 24 + walltime_hard = 24 + memlimit = 10 + inputs = {} + expected_leader_args = "--time={} --mem={}G".format(walltime, memlimit) + expected_job_group = "--wckey={}".format(job_id) + expected_tool_args = "--time={}".format(walltime_hard) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + job_group = " ".join(jobsubmitterObject._job_group()) + tool_args_list = jobsubmitterObject._tool_args() + tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) + self.assertEqual(leader_args, expected_leader_args) + self.assertEqual(job_group, expected_job_group) + self.assertEqual(tool_args, expected_tool_args) @patch("orchestrator.tasks.command_processor.delay") @patch("orchestrator.tasks.get_job_info_path") @patch("batch_systems.lsf_client.lsf_client.LSFClient.status") @patch("submitter.toil_submitter.ToilJobSubmitter.get_outputs") @patch("orchestrator.tasks.set_permissions_job.delay") - def test_complete(self, permission, get_outputs, status, get_job_info_path, command_processor): - self.current_job.status = Status.PENDING - self.current_job.save() - permission.return_value = None - command_processor.return_value = True - get_outputs.return_value = {"outputs": True}, None - get_job_info_path.return_value = "sample/job/path" - status.return_value = Status.COMPLETED, None - check_job_status(self.current_job) - self.current_job.refresh_from_db() - self.assertEqual(self.current_job.status, Status.SET_PERMISSIONS) - self.assertNotEqual(self.current_job.finished, None) + def test_complete_lsf(self, permission, get_outputs, status, get_job_info_path, command_processor): + with override_settings(BATCH_SYSTEM="LSF"): + self.current_job.status = Status.PENDING + self.current_job.save() + permission.return_value = None + command_processor.return_value = True + get_outputs.return_value = {"outputs": True}, None + get_job_info_path.return_value = "sample/job/path" + status.return_value = Status.COMPLETED, None + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.SET_PERMISSIONS) + self.assertNotEqual(self.current_job.finished, None) + + @patch("orchestrator.tasks.command_processor.delay") + @patch("orchestrator.tasks.get_job_info_path") + @patch("batch_systems.slurm_client.slurm_client.SLURMClient.status") + @patch("submitter.toil_submitter.ToilJobSubmitter.get_outputs") + @patch("orchestrator.tasks.set_permissions_job.delay") + def test_complete_slurm(self, permission, get_outputs, status, get_job_info_path, command_processor): + with override_settings(BATCH_SYSTEM="SLURM"): + self.current_job.status = Status.PENDING + self.current_job.save() + permission.return_value = None + command_processor.return_value = True + get_outputs.return_value = {"outputs": True}, None + get_job_info_path.return_value = "sample/job/path" + status.return_value = Status.COMPLETED, None + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.SET_PERMISSIONS) + self.assertNotEqual(self.current_job.finished, None) @patch("orchestrator.tasks.command_processor.delay") @patch("orchestrator.tasks.get_job_info_path") @patch("batch_systems.lsf_client.lsf_client.LSFClient.status") - def test_fail(self, status, get_job_info_path, command_processor): - self.current_job.status = Status.PENDING - self.current_job.save() - command_processor.return_value = True - get_job_info_path.return_value = "sample/job/path" - status.return_value = Status.FAILED, "submitter reason" - check_job_status(self.current_job) - self.current_job.refresh_from_db() - self.assertEqual(self.current_job.status, Status.FAILED) - self.assertNotEqual(self.current_job.finished, None) - info_message = self.current_job.message["info"] - failed_jobs = self.current_job.message["failed_jobs"] - unknown_jobs = self.current_job.message["unknown_jobs"] - expected_failed_jobs = { - "failed_job_1": ["failed_job_1_id"], - "failed_job_2": ["failed_job_2_id"], - "running_job": ["running_job_id"], - } - expected_unknown_jobs = {"unknown_job": ["unknown_job_id_1", "unknown_job_id_2"]} - self.assertEqual(info_message, "submitter reason") - self.assertEqual(failed_jobs, expected_failed_jobs) - self.assertEqual(unknown_jobs, expected_unknown_jobs) + def test_fail_lsf(self, status, get_job_info_path, command_processor): + with override_settings(BATCH_SYSTEM="LSF"): + self.current_job.status = Status.PENDING + self.current_job.save() + command_processor.return_value = True + get_job_info_path.return_value = "sample/job/path" + status.return_value = Status.FAILED, "submitter reason" + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.FAILED) + self.assertNotEqual(self.current_job.finished, None) + info_message = self.current_job.message["info"] + failed_jobs = self.current_job.message["failed_jobs"] + unknown_jobs = self.current_job.message["unknown_jobs"] + expected_failed_jobs = { + "failed_job_1": ["failed_job_1_id"], + "failed_job_2": ["failed_job_2_id"], + "running_job": ["running_job_id"], + } + expected_unknown_jobs = {"unknown_job": ["unknown_job_id_1", "unknown_job_id_2"]} + self.assertEqual(info_message, "submitter reason") + self.assertEqual(failed_jobs, expected_failed_jobs) + self.assertEqual(unknown_jobs, expected_unknown_jobs) + + @patch("orchestrator.tasks.command_processor.delay") + @patch("orchestrator.tasks.get_job_info_path") + @patch("batch_systems.slurm_client.slurm_client.SLURMClient.status") + def test_fail_slurm(self, status, get_job_info_path, command_processor): + with override_settings(BATCH_SYSTEM="SLURM"): + self.current_job.status = Status.PENDING + self.current_job.save() + command_processor.return_value = True + get_job_info_path.return_value = "sample/job/path" + status.return_value = Status.FAILED, "submitter reason" + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.FAILED) + self.assertNotEqual(self.current_job.finished, None) + info_message = self.current_job.message["info"] + failed_jobs = self.current_job.message["failed_jobs"] + unknown_jobs = self.current_job.message["unknown_jobs"] + expected_failed_jobs = { + "failed_job_1": ["failed_job_1_id"], + "failed_job_2": ["failed_job_2_id"], + "running_job": ["running_job_id"], + } + expected_unknown_jobs = {"unknown_job": ["unknown_job_id_1", "unknown_job_id_2"]} + self.assertEqual(info_message, "submitter reason") + self.assertEqual(failed_jobs, expected_failed_jobs) + self.assertEqual(unknown_jobs, expected_unknown_jobs) @patch("orchestrator.tasks.command_processor.delay") @patch("orchestrator.tasks.get_job_info_path") @patch("batch_systems.lsf_client.lsf_client.LSFClient.status") - def test_running(self, status, get_job_info_path, command_processor): - self.current_job.status = Status.PENDING - self.current_job.save() - command_processor.return_value = True - get_job_info_path.return_value = "sample/job/path" - status.return_value = Status.RUNNING, None - check_job_status(self.current_job) - self.current_job.refresh_from_db() - self.assertEqual(self.current_job.status, Status.RUNNING) - self.assertNotEqual(self.current_job.started, None) - self.assertEqual(self.current_job.finished, None) + def test_running_lsf(self, status, get_job_info_path, command_processor): + with override_settings(BATCH_SYSTEM="LSF"): + self.current_job.status = Status.PENDING + self.current_job.save() + command_processor.return_value = True + get_job_info_path.return_value = "sample/job/path" + status.return_value = Status.RUNNING, None + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.RUNNING) + self.assertNotEqual(self.current_job.started, None) + self.assertEqual(self.current_job.finished, None) + + @patch("orchestrator.tasks.command_processor.delay") + @patch("orchestrator.tasks.get_job_info_path") + @patch("batch_systems.slurm_client.slurm_client.SLURMClient.status") + def test_running_slurm(self, status, get_job_info_path, command_processor): + with override_settings(BATCH_SYSTEM="SLURM"): + self.current_job.status = Status.PENDING + self.current_job.save() + command_processor.return_value = True + get_job_info_path.return_value = "sample/job/path" + status.return_value = Status.RUNNING, None + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.RUNNING) + self.assertNotEqual(self.current_job.started, None) + self.assertEqual(self.current_job.finished, None) @patch("orchestrator.tasks.command_processor.delay") @patch("batch_systems.lsf_client.lsf_client.LSFClient.status") @skip("We are no longer failing tests on pending status, and instead letting the task fail it") - def test_fail_not_submitted(self, status, command_processor): - command_processor.return_value = True - status.return_value = Status.PENDING, None - self.current_job.status = Status.PENDING - self.current_job.external_id = None - self.current_job.save() - check_job_status(self.current_job) - self.current_job.refresh_from_db() - self.assertEqual(self.current_job.status, Status.FAILED) - self.assertNotEqual(self.current_job.finished, None) - info_message = self.current_job.message["info"] - failed_jobs = self.current_job.message["failed_jobs"] - unknown_jobs = self.current_job.message["unknown_jobs"] - expected_failed_jobs = {} - expected_unknown_jobs = {} - self.assertTrue("External id not provided" in info_message) - self.assertEqual(failed_jobs, expected_failed_jobs) - self.assertEqual(unknown_jobs, expected_unknown_jobs) + def test_fail_not_submitted_lsf(self, status, command_processor): + with override_settings(BATCH_SYSTEM="LSF"): + command_processor.return_value = True + status.return_value = Status.PENDING, None + self.current_job.status = Status.PENDING + self.current_job.external_id = None + self.current_job.save() + check_job_status(self.current_job) + self.current_job.refresh_from_db() + self.assertEqual(self.current_job.status, Status.FAILED) + self.assertNotEqual(self.current_job.finished, None) + info_message = self.current_job.message["info"] + failed_jobs = self.current_job.message["failed_jobs"] + unknown_jobs = self.current_job.message["unknown_jobs"] + expected_failed_jobs = {} + expected_unknown_jobs = {} + self.assertTrue("External id not provided" in info_message) + self.assertEqual(failed_jobs, expected_failed_jobs) + self.assertEqual(unknown_jobs, expected_unknown_jobs) @patch("orchestrator.tasks.cleanup_folders") - def test_cleanup(self, cleanup_folders): - Job.objects.create( - type=PipelineType.CWL, - app={"app": "link"}, - status=Status.COMPLETED, - created_date=datetime.now() - timedelta(days=1), - finished=datetime.now() - timedelta(days=1), - ) - testtime = datetime.now() - timedelta(days=32) - with patch("django.utils.timezone.now") as mock_now: - mock_now.return_value = testtime - job_old_completed = Job.objects.create( - type=PipelineType.CWL, app={"app": "link"}, status=Status.COMPLETED, finished=testtime + def test_cleanup_lsf(self, cleanup_folders): + with override_settings(BATCH_SYSTEM="LSF"): + Job.objects.create( + type=PipelineType.CWL, + app={"app": "link"}, + status=Status.COMPLETED, + created_date=datetime.now() - timedelta(days=1), + finished=datetime.now() - timedelta(days=1), ) - job_old_failed = Job.objects.create( - type=PipelineType.CWL, app={"app": "link"}, status=Status.FAILED, finished=testtime + testtime = datetime.now() - timedelta(days=32) + with patch("django.utils.timezone.now") as mock_now: + mock_now.return_value = testtime + job_old_completed = Job.objects.create( + type=PipelineType.CWL, app={"app": "link"}, status=Status.COMPLETED, finished=testtime + ) + job_old_failed = Job.objects.create( + type=PipelineType.CWL, app={"app": "link"}, status=Status.FAILED, finished=testtime + ) + + cleanup_completed_jobs() + cleanup_failed_jobs() + + calls = [ + call(str(job_old_completed.id), exclude=["input.json", "lsf.log"]), + call(str(job_old_failed.id), exclude=["input.json", "lsf.log"]), + ] + + cleanup_folders.delay.assert_has_calls(calls, any_order=True) + + @patch("orchestrator.tasks.cleanup_folders") + def test_cleanup_slurm(self, cleanup_folders): + with override_settings(BATCH_SYSTEM="SLURM"): + Job.objects.create( + type=PipelineType.CWL, + app={"app": "link"}, + status=Status.COMPLETED, + created_date=datetime.now() - timedelta(days=1), + finished=datetime.now() - timedelta(days=1), ) + testtime = datetime.now() - timedelta(days=32) + with patch("django.utils.timezone.now") as mock_now: + mock_now.return_value = testtime + job_old_completed = Job.objects.create( + type=PipelineType.CWL, app={"app": "link"}, status=Status.COMPLETED, finished=testtime + ) + job_old_failed = Job.objects.create( + type=PipelineType.CWL, app={"app": "link"}, status=Status.FAILED, finished=testtime + ) - cleanup_completed_jobs() - cleanup_failed_jobs() + cleanup_completed_jobs() + cleanup_failed_jobs() - calls = [ - call(str(job_old_completed.id), exclude=["input.json", "lsf.log"]), - call(str(job_old_failed.id), exclude=["input.json", "lsf.log"]), - ] + calls = [ + call(str(job_old_completed.id), exclude=["input.json", "slurm.log"]), + call(str(job_old_failed.id), exclude=["input.json", "slurm.log"]), + ] - cleanup_folders.delay.assert_has_calls(calls, any_order=True) + cleanup_folders.delay.assert_has_calls(calls, any_order=True) From 51b42dbb88311a1c6022ec6d097e85cded67002f Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 2 May 2025 13:25:42 -0400 Subject: [PATCH 080/235] Removed unused import --- tests/test_tasks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 74f428e1..b6f6440b 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -13,7 +13,6 @@ from mock import patch, call import uuid from batch_systems.lsf_client.lsf_client import format_lsf_job_id -from django.conf import settings from submitter.toil_submitter import ToilJobSubmitter MAX_RUNNING_JOBS = 3 From 31c246f505541615248cacc44ef9d9b5c37e8469 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 2 May 2025 20:55:40 -0400 Subject: [PATCH 081/235] Updated python build in the Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index f338311c..960c83b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,8 +27,8 @@ RUN apt-get update \ # Install python packages && pip3 install --upgrade pip \ && pip3 install python-ldap \ - && pip3 install -r requirements.txt \ - && pip3 install -r requirements-toil.txt \ + && pip3 install --use-pep517 -r requirements.txt \ + && pip3 install --use-pep517 -r requirements-toil.txt \ # Clean up image && apt-get -y purge --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* From d6251d383ee2ed4646afe6b28f2ae804c2340790 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 5 May 2025 12:02:46 -0400 Subject: [PATCH 082/235] Remove stats so toil will clean jobstore onSuccess --- submitter/toil_submitter/toil_jobsubmitter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 76500fb4..5523857a 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -275,7 +275,6 @@ def _command_line(self): str(settings.TOIL_STATE_POLLING_WAIT), "--disable-user-provenance", "--disable-host-provenance", - "--stats", "--cleanWorkDir", "onSuccess", "--disableProgress", @@ -326,7 +325,6 @@ def _command_line(self): str(settings.TOIL_STATE_POLLING_WAIT), "--disable-user-provenance", "--disable-host-provenance", - "--stats", "--cleanWorkDir", "onSuccess", "--disableProgress", From ea2ee9a8973fc1311212951d414601ae475ae397 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 5 May 2025 12:03:09 -0400 Subject: [PATCH 083/235] Added output parser for new TOIL --- submitter/toil_submitter/toil_jobsubmitter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 5523857a..8b188f6b 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -137,7 +137,11 @@ def get_outputs(self): data = f.readlines() data = "".join(data) substring = data.split("\n{")[1] - result = ("{" + substring).split("-----------")[0] + if "-----------" in substring: + result = ("{" + substring).split("-----------")[0] + else: + result_segment = substring.split("}[")[0] + result = "{" + result_segment + "}" result_json = json.loads(result) except (IndexError, ValueError): error_message = "Could not parse json from %s" % log_path From d8c96514a5fce966fddbc22eeeea2ccd5d9503c4 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 6 May 2025 14:14:43 -0400 Subject: [PATCH 084/235] Version bump --- ridgeback/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/__init__.py b/ridgeback/__init__.py index 4ee5c8be..773c90f9 100644 --- a/ridgeback/__init__.py +++ b/ridgeback/__init__.py @@ -1 +1 @@ -__version__ = "1.38.0" +__version__ = "1.39.0" From 8624c51a0662f3b35d6bee326f461b0c43aa3d49 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 6 May 2025 15:27:53 -0400 Subject: [PATCH 085/235] Added back debug flag --- submitter/toil_submitter/toil_jobsubmitter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index e6ca4cd5..521d85e6 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -293,6 +293,7 @@ def _command_line(self): "--disable-host-provenance", "--cleanWorkDir", "onSuccess", + "--debug", "--disableProgress", "--doubleMem", "True", @@ -343,6 +344,7 @@ def _command_line(self): "--disable-host-provenance", "--cleanWorkDir", "onSuccess", + "--debug", "--disableProgress", "--doubleMem", "True", From 43dfa6cc894feb91bac20043160beb7a33a63a2c Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 7 May 2025 15:06:05 -0400 Subject: [PATCH 086/235] Revert "Added back debug flag" --debug is no longer supported This reverts commit 8624c51a0662f3b35d6bee326f461b0c43aa3d49. --- submitter/toil_submitter/toil_jobsubmitter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 521d85e6..e6ca4cd5 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -293,7 +293,6 @@ def _command_line(self): "--disable-host-provenance", "--cleanWorkDir", "onSuccess", - "--debug", "--disableProgress", "--doubleMem", "True", @@ -344,7 +343,6 @@ def _command_line(self): "--disable-host-provenance", "--cleanWorkDir", "onSuccess", - "--debug", "--disableProgress", "--doubleMem", "True", From bf9480d944ab75b18ad4be8fca2f02510956235d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 12 May 2025 23:06:27 -0400 Subject: [PATCH 087/235] Update toil to not kill slow start jobs --- submitter/toil_submitter/toil_jobsubmitter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index e6ca4cd5..0295fb5a 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -307,8 +307,8 @@ def _command_line(self): settings.TOIL_DEFAULT_MEMORY, "--maxCores", settings.TOIL_MAX_CORES, - "--maxDisk", - "128G", + "--jobStoreTimeout", + "600", "--maxMemory", "256G", "--not-strict", @@ -358,8 +358,8 @@ def _command_line(self): settings.TOIL_DEFAULT_MEMORY, "--maxCores", settings.TOIL_MAX_CORES, - "--maxDisk", - "128G", + "--jobStoreTimeout", + "600", "--maxMemory", "256G", "--not-strict", From 00dd73c9f4626aae18f4e6ee52943db7fb0e510d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 13 May 2025 01:05:40 -0400 Subject: [PATCH 088/235] Add argument to set number of tasks --- batch_systems/batch_system.py | 7 +++++++ batch_systems/lsf_client/lsf_client.py | 11 +++++++++++ batch_systems/slurm_client/slurm_client.py | 11 +++++++++++ submitter/toil_submitter/toil_jobsubmitter.py | 10 ++++++++-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index cb90bf9f..bbf34f21 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -71,6 +71,13 @@ def set_memlimit(self, mem_limit, default=None): mem_limit_args = [] return mem_limit_args + def set_num_tasks(self, num_tasks, default=None): + """ + Set the number of tasks for the batch job + """ + num_task_args = [] + return num_task_args + def set_group(self, group_id): """ Set the group args of the batch job diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index 5d161612..86980a4a 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -105,6 +105,17 @@ def set_memlimit(self, mem_limit, default=None): mem_limit_args = ["-M", mem_limit] return mem_limit_args + def set_num_tasks(self, num_tasks, default=None): + """ + Set the number of tasks for the batch job + """ + num_task_args = [] + if default: + num_task_args = ["-n", default] + if num_tasks: + num_task_args = ["-n", num_tasks] + return num_task_args + def set_group(self, group_id): group_id_args = [] if group_id: diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index b4f36cd9..59eff39e 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -111,6 +111,17 @@ def set_memlimit(self, mem_limit, default=None): mem_limit_args = [f"--mem={mem_limit}G"] return mem_limit_args + def set_num_tasks(self, num_tasks, default=None): + """ + Set the number of tasks for the batch job + """ + num_task_args = [] + if default: + num_task_args = [f"--cpus-per-task={default}"] + if num_tasks: + num_task_args = [f"--cpus-per-task={num_tasks}"] + return num_task_args + def set_group(self, group_id): group_id_args = [] if group_id: diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 0295fb5a..a17364fa 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -66,6 +66,7 @@ def __init__( self.job_tmp_dir = os.path.join(dir_config["TMP_DIR_ROOT"], self.job_id) self.batch_system = get_batch_system() self.batch_system_args_env = None + self.single_machine_mode_workflows = ["nucleo_qc", "argos-qc"] if settings.BATCH_SYSTEM == "LSF": self.batch_system_args_env = "TOIL_LSF_ARGS" elif settings.BATCH_SYSTEM == "SLURM": @@ -205,8 +206,11 @@ def _prepare_directories(self): os.mkdir(self.job_tmp_dir) def _leader_args(self): + single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) args = self._walltime() args.extend(self._memlimit()) + if single_machine: + args.extend(self._numtasks(7)) return args def _tool_args(self): @@ -226,12 +230,14 @@ def _walltime(self): def _memlimit(self): return self.batch_system.set_memlimit(self.memlimit) + def _numtasks(self, num_tasks): + return self.batch_system.set_num_tasks(num_tasks) + def _job_group(self): return self.batch_system.set_group(self.job_id) def _command_line(self): - single_machine_mode_workflows = ["nucleo_qc", "argos-qc"] - single_machine = any([w in self.app.github.lower() for w in single_machine_mode_workflows]) + single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) if settings.ACCESS_LEGACY_APP in self.app.github.lower(): """ Start ACCESS-specific code From d7a675bfe8921d60f075d4cad26adb2bdaa88cab Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 13 May 2025 14:32:27 -0400 Subject: [PATCH 089/235] Add ssh to docker container --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 960c83b5..a7abf820 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,6 +31,7 @@ RUN apt-get update \ && pip3 install --use-pep517 -r requirements-toil.txt \ # Clean up image && apt-get -y purge --auto-remove build-essential \ + && apt-get -y --no-install-recommends install openssh-client \ && rm -rf /var/lib/apt/lists/* From 11e4e9c7d91c843bd172bdcf87053bd4194f0770 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 14 May 2025 09:51:06 -0400 Subject: [PATCH 090/235] Added another cwl output parser --- submitter/toil_submitter/toil_jobsubmitter.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index a17364fa..bb60bdee 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -151,6 +151,9 @@ def get_outputs(self): substring = data.split("\n{")[1] if "-----------" in substring: result = ("{" + substring).split("-----------")[0] + elif "\n}\n" in substring: + result_segment = substring.split("\n}\n")[0] + result = "{" + result_segment + "}" else: result_segment = substring.split("}[")[0] result = "{" + result_segment + "}" From ca9659d081f565ddc5b488c559727db27fb44c0e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 3 Jun 2025 17:58:28 -0400 Subject: [PATCH 091/235] Bumped max cores to 40 to allow certain pipelines to run --- ridgeback/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 88e3d98f..f2d99b4b 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -285,7 +285,7 @@ CWLTOIL = os.environ.get("RIDGEBACK_TOIL", "toil-cwl-runner") TOIL_STATE_POLLING_WAIT = os.environ.get("TOIL_STATE_POLLING_WAIT", 60) -TOIL_MAX_CORES = os.environ.get("RIDGEBACK_TOIL_MAX_CORES", "24") +TOIL_MAX_CORES = os.environ.get("RIDGEBACK_TOIL_MAX_CORES", "40") TOIL_DEFAULT_MEMORY = os.environ.get("RIDGEBACK_TOIL_DEFAULT_MEMORY", "8G") # Nextflow settings From 518d19b3990ee90e49e327a90dae41c767bc8ee6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 3 Jun 2025 19:01:17 -0400 Subject: [PATCH 092/235] Made single machine cores configurable --- ridgeback/settings.py | 1 + submitter/toil_submitter/toil_jobsubmitter.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index f2d99b4b..60dd657e 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -287,6 +287,7 @@ TOIL_STATE_POLLING_WAIT = os.environ.get("TOIL_STATE_POLLING_WAIT", 60) TOIL_MAX_CORES = os.environ.get("RIDGEBACK_TOIL_MAX_CORES", "40") TOIL_DEFAULT_MEMORY = os.environ.get("RIDGEBACK_TOIL_DEFAULT_MEMORY", "8G") +SINGLE_MACHINE_CORES = os.environ.get("RIDGEBACK_SINGLE_MACHINE_CORES", 16) # Nextflow settings diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index bb60bdee..de2c0512 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -213,7 +213,7 @@ def _leader_args(self): args = self._walltime() args.extend(self._memlimit()) if single_machine: - args.extend(self._numtasks(7)) + args.extend(self._numtasks(int(settings.SINGLE_MACHINE_CORES))) return args def _tool_args(self): From a201856dee7e8a3aac9b82058b65490ed5984d71 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 9 Jun 2025 16:54:42 -0400 Subject: [PATCH 093/235] Bypass filestore creation to reduce disk footprint --- submitter/toil_submitter/toil_jobsubmitter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index de2c0512..6aacae78 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -335,7 +335,7 @@ def _command_line(self): "--maxLocalJobs", "500", "--no-prepull", - "--reference-inputs", + "--bypass-file-store", ] else: command_line = [ @@ -386,7 +386,7 @@ def _command_line(self): "--maxLocalJobs", "500", "--no-prepull", - "--reference-inputs", + "--bypass-file-store", ] if self.resume_jobstore: command_line.extend(["--restart", self.app_location]) From 80589bb9a90f5e8177e4ba3771fe2434fc9fe21e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 11 Jun 2025 10:42:45 -0400 Subject: [PATCH 094/235] Remove bypass-filestore as it introduces run instability --- submitter/toil_submitter/toil_jobsubmitter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 6aacae78..de2c0512 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -335,7 +335,7 @@ def _command_line(self): "--maxLocalJobs", "500", "--no-prepull", - "--bypass-file-store", + "--reference-inputs", ] else: command_line = [ @@ -386,7 +386,7 @@ def _command_line(self): "--maxLocalJobs", "500", "--no-prepull", - "--bypass-file-store", + "--reference-inputs", ] if self.resume_jobstore: command_line.extend(["--restart", self.app_location]) From 9737454a91ea1ce907e968513bc9e418bade509b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 11 Jun 2025 10:45:25 -0400 Subject: [PATCH 095/235] Update handling walltime in nextflow --- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 4765ae97..7309d109 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -100,7 +100,7 @@ def _leader_args(self): return args def _walltime(self): - return self.batch_system.set_walltime(self.walltime) + return self.batch_system.set_walltime(None, self.walltime) def _memlimit(self): return self.batch_system.set_memlimit(self.memlimit, default="20") From a02a7ca4a9877c8b74f7ec3ff2469ab93d59e7a7 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 16 Jun 2025 16:17:13 -0400 Subject: [PATCH 096/235] Set default mem_limit in Slurm --- batch_systems/slurm_client/slurm_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 59eff39e..50e4ce02 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -106,7 +106,7 @@ def set_walltime(self, expected_limit, hard_limit): def set_memlimit(self, mem_limit, default=None): mem_limit_args = [] if default: - mem_limit = [f"--mem={default}G"] + return [f"--mem={default}G"] if mem_limit: mem_limit_args = [f"--mem={mem_limit}G"] return mem_limit_args From d4a285cbcd9829c18f54e81b42a89322a4b1c5c3 Mon Sep 17 00:00:00 2001 From: buehlere Date: Mon, 7 Jul 2025 14:13:15 -0400 Subject: [PATCH 097/235] new virtual environment --- submitter/toil_submitter/toil_jobsubmitter.py | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index de2c0512..aea8c079 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -245,36 +245,42 @@ def _command_line(self): """ Start ACCESS-specific code """ - access_path = "PATH=/home/accessbot/miniconda3/envs/ACCESS_cmplx_geno_test/bin:{}" + access_path = "PATH=/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin:{}" path = access_path.format(os.environ.get("PATH")) command_line = [ path, "toil-cwl-runner", "--no-container", + "--coalesceStatusCalls", "--logFile", "toil_log.log", "--batchSystem", self.batch_system.name, - "--logLevel", - "DEBUG", - "--stats", + "--disable-user-provenance", + "--disable-host-provenance", "--cleanWorkDir", "onSuccess", + "--disableProgress", + "--doubleMem", + "True", "--disableCaching", - "--defaultMemory", - "10G", - "--retryCount", - "2", - "--disableChaining", "--preserve-environment", "PATH", "TMPDIR", self.batch_system_args_env, "CWL_SINGULARITY_CACHE", + "SINGULARITYENV_LC_ALL", "PWD", - "_JAVA_OPTIONS", - "PYTHONPATH", - "TEMP", + "--maxCores", + "24", + "--jobStoreTimeout", + "600", + "--maxMemory", + "256G", + "--not-strict", + "--runCwlInternalJobsOnWorkers", + "--realTimeLogging", + "True", "--jobStore", self.job_store_dir, "--tmpdir-prefix", @@ -283,6 +289,10 @@ def _command_line(self): self.job_work_dir, "--outdir", self.job_outputs_dir, + "--maxLocalJobs", + "500", + "--no-prepull", + "--reference-inputs", ] """ End ACCESS-specific code From 74af65f871eeb79153ac1f5e96031b7c6a02128a Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 8 Jul 2025 16:21:31 -0400 Subject: [PATCH 098/235] remove new toil arguments --- submitter/toil_submitter/toil_jobsubmitter.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index aea8c079..7791cfb5 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -251,7 +251,6 @@ def _command_line(self): path, "toil-cwl-runner", "--no-container", - "--coalesceStatusCalls", "--logFile", "toil_log.log", "--batchSystem", @@ -260,17 +259,19 @@ def _command_line(self): "--disable-host-provenance", "--cleanWorkDir", "onSuccess", - "--disableProgress", - "--doubleMem", - "True", "--disableCaching", "--preserve-environment", - "PATH", + "PATH", "TMPDIR", + "PWD", + "_JAVA_OPTIONS", + "PYTHONPATH", + "TEMP", self.batch_system_args_env, "CWL_SINGULARITY_CACHE", "SINGULARITYENV_LC_ALL", "PWD", + "--disableChaining" "--maxCores", "24", "--jobStoreTimeout", @@ -291,8 +292,6 @@ def _command_line(self): self.job_outputs_dir, "--maxLocalJobs", "500", - "--no-prepull", - "--reference-inputs", ] """ End ACCESS-specific code From 50305b30d44b2e290a1a6bd8e7a072a7b6d656a1 Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 8 Jul 2025 16:24:50 -0400 Subject: [PATCH 099/235] black format --- submitter/toil_submitter/toil_jobsubmitter.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 7791cfb5..22479d6b 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -261,7 +261,7 @@ def _command_line(self): "onSuccess", "--disableCaching", "--preserve-environment", - "PATH", + "PATH", "TMPDIR", "PWD", "_JAVA_OPTIONS", @@ -271,8 +271,7 @@ def _command_line(self): "CWL_SINGULARITY_CACHE", "SINGULARITYENV_LC_ALL", "PWD", - "--disableChaining" - "--maxCores", + "--disableChaining" "--maxCores", "24", "--jobStoreTimeout", "600", From 9c5b9938b35c0efb737741b35d7922b909050822 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 8 Jul 2025 17:32:50 -0400 Subject: [PATCH 100/235] Update pep spec in requirements-toil.txt --- requirements-toil.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-toil.txt b/requirements-toil.txt index b4d5fbcd..8d4a12af 100644 --- a/requirements-toil.txt +++ b/requirements-toil.txt @@ -1 +1 @@ -git+https://github.com/DataBiosphere/toil.git@releases/8.0.0#egg=toil[cwl] +toil[cwl] @ git+https://github.com/DataBiosphere/toil.git@releases/8.0.0 From 19da54a5b8d03d279b755bb377df656db1817fcd Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 16 Jul 2025 14:42:17 -0400 Subject: [PATCH 101/235] get conda env from settings --- ridgeback/settings.py | 1 + submitter/toil_submitter/toil_jobsubmitter.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 60dd657e..74390f0f 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -317,3 +317,4 @@ # ACCESS LEGACY INFO ACCESS_LEGACY_APP = os.environ.get("ACCESS_LEGACY_APP", "access-pipeline") +ACCESS_LEGACY_CONDA_ENV = os.environ.get("ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin") diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 22479d6b..41d43665 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -245,8 +245,7 @@ def _command_line(self): """ Start ACCESS-specific code """ - access_path = "PATH=/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin:{}" - path = access_path.format(os.environ.get("PATH")) + path = "PATH={0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) command_line = [ path, "toil-cwl-runner", From 3b8c307e7ec94786bd462b76ba74f0716eaf1390 Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 23 Jul 2025 10:48:36 -0400 Subject: [PATCH 102/235] add env to xsv1 command --- submitter/toil_submitter/toil_jobsubmitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 41d43665..9aed8e6f 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -245,7 +245,7 @@ def _command_line(self): """ Start ACCESS-specific code """ - path = "PATH={0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) + path = "env PATH={0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) command_line = [ path, "toil-cwl-runner", From 61bbf729fe364d0d8bbb060d88d2af6147c8e13f Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 23 Jul 2025 10:53:47 -0400 Subject: [PATCH 103/235] fix settings format --- ridgeback/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 74390f0f..77836d99 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -317,4 +317,6 @@ # ACCESS LEGACY INFO ACCESS_LEGACY_APP = os.environ.get("ACCESS_LEGACY_APP", "access-pipeline") -ACCESS_LEGACY_CONDA_ENV = os.environ.get("ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin") +ACCESS_LEGACY_CONDA_ENV = os.environ.get( + "ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin" +) From 7f43108db5c079d68a430e445532e4f0ad6299ec Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 23 Jul 2025 13:40:10 -0400 Subject: [PATCH 104/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 9aed8e6f..455adc56 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -270,7 +270,8 @@ def _command_line(self): "CWL_SINGULARITY_CACHE", "SINGULARITYENV_LC_ALL", "PWD", - "--disableChaining" "--maxCores", + "--disableChaining", + "--maxCores", "24", "--jobStoreTimeout", "600", From 0bfde6776d1ff897a0fde344f154a276fc75ed0f Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 23 Jul 2025 17:34:27 -0400 Subject: [PATCH 105/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 455adc56..8f4b0589 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -273,14 +273,11 @@ def _command_line(self): "--disableChaining", "--maxCores", "24", - "--jobStoreTimeout", - "600", "--maxMemory", "256G", "--not-strict", "--runCwlInternalJobsOnWorkers", "--realTimeLogging", - "True", "--jobStore", self.job_store_dir, "--tmpdir-prefix", From 6073f552fb2e0d765bb67403e28354fab340e23d Mon Sep 17 00:00:00 2001 From: buehlere Date: Fri, 25 Jul 2025 12:00:05 -0400 Subject: [PATCH 106/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 8f4b0589..e63f8622 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -89,6 +89,9 @@ def get_submit_command(self): ) env["JAVA_HOME"] = None env[self.batch_system_args_env] = toil_batch_system_args.strip() + if settings.ACCESS_LEGACY_APP in self.app.github.lower() and settings.BATCH_SYSTEM == "SLURM": + env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) + env[self.batch_system_args_env] += "--export=ALL" return command_line, self._leader_args(), log_path, self.job_id, env def get_commandline_status(self, cache): @@ -241,13 +244,11 @@ def _job_group(self): def _command_line(self): single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) - if settings.ACCESS_LEGACY_APP in self.app.github.lower(): + if settings.ACCESS_LEGACY_APP in self.app.github.lower() and settings.BATCH_SYSTEM == "SLURM": """ Start ACCESS-specific code """ - path = "env PATH={0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) command_line = [ - path, "toil-cwl-runner", "--no-container", "--logFile", From 25cb603294b6c821b1f2260182cb78d6dcdb0c4e Mon Sep 17 00:00:00 2001 From: buehlere Date: Fri, 25 Jul 2025 12:00:49 -0400 Subject: [PATCH 107/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index e63f8622..c9354f00 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -89,7 +89,7 @@ def get_submit_command(self): ) env["JAVA_HOME"] = None env[self.batch_system_args_env] = toil_batch_system_args.strip() - if settings.ACCESS_LEGACY_APP in self.app.github.lower() and settings.BATCH_SYSTEM == "SLURM": + if settings.ACCESS_LEGACY_APP in self.app.github.lower(): env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) env[self.batch_system_args_env] += "--export=ALL" return command_line, self._leader_args(), log_path, self.job_id, env From db57f1cdfdefdb2379a3084b5a29ee9bf7565131 Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 30 Jul 2025 10:06:51 -0400 Subject: [PATCH 108/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index c9354f00..607733a9 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -244,7 +244,7 @@ def _job_group(self): def _command_line(self): single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) - if settings.ACCESS_LEGACY_APP in self.app.github.lower() and settings.BATCH_SYSTEM == "SLURM": + if settings.ACCESS_LEGACY_APP in self.app.github.lower(): """ Start ACCESS-specific code """ @@ -268,14 +268,17 @@ def _command_line(self): "PYTHONPATH", "TEMP", self.batch_system_args_env, - "CWL_SINGULARITY_CACHE", - "SINGULARITYENV_LC_ALL", "PWD", + "TOIL_SLURM_ARGS", "--disableChaining", "--maxCores", "24", "--maxMemory", "256G", + "--defaultMemory", + "10G", + "--defaultDisk", + "20G", "--not-strict", "--runCwlInternalJobsOnWorkers", "--realTimeLogging", From a065e4e6b92e889c998ef5051b4a518a2b7f50f4 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Jul 2025 14:10:07 -0400 Subject: [PATCH 109/235] Added user model to job and userswitcher function --- orchestrator/migrations/0021_job_user.py | 18 ++++++ orchestrator/models.py | 2 + submitter/userswitcher.py | 71 ++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 orchestrator/migrations/0021_job_user.py create mode 100644 submitter/userswitcher.py diff --git a/orchestrator/migrations/0021_job_user.py b/orchestrator/migrations/0021_job_user.py new file mode 100644 index 00000000..5820092c --- /dev/null +++ b/orchestrator/migrations/0021_job_user.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2025-07-30 18:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orchestrator', '0020_auto_20250402_0942'), + ] + + operations = [ + migrations.AddField( + model_name='job', + name='user', + field=models.CharField(default='kumarn1', max_length=100), + ), + ] diff --git a/orchestrator/models.py b/orchestrator/models.py index 9e7b0cd7..6eacce1a 100755 --- a/orchestrator/models.py +++ b/orchestrator/models.py @@ -7,6 +7,7 @@ from django.utils.dateparse import parse_datetime from django.utils.timezone import is_aware, make_aware, now from django.conf import settings +from getpass import getuser logger = logging.getLogger(__name__) @@ -167,6 +168,7 @@ class Job(BaseModel): base_dir = models.CharField(max_length=1000) root_dir = models.CharField(max_length=1000) root_permission = models.CharField(default=settings.OUTPUT_DEFAULT_PERMISSION, max_length=3) + user = models.CharField(default=getuser(), max_length=100) output_uid = models.IntegerField(default=settings.OUTPUT_DEFAULT_UID, editable=True) output_gid = models.IntegerField(default=settings.OUTPUT_DEFAULT_GID, editable=True) job_store_location = models.CharField(max_length=1000, null=True, blank=True) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py new file mode 100644 index 00000000..fb99d974 --- /dev/null +++ b/submitter/userswitcher.py @@ -0,0 +1,71 @@ +import sys +import marshal +import subprocess +import dill +import contextlib +import io +import logging +from pathlib import Path +from functools import wraps +from getpass import getuser + +log = logging.getLogger(__name__) + + +def userscript(): + stdout_buffer = io.StringIO() + stderr_buffer = io.StringIO() + exception_raised = False + with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): + try: + func_serialized, args, kwargs = marshal.loads(sys.stdin.buffer.read()) + func = dill.loads(func_serialized) + output = func(*args, **kwargs) + except Exception as e: + log.exception("Exception when running the function as another user") + exception_raised = True + script_tuple = (output, stdout_buffer.getvalue()) + sys.stderr.buffer.write(stderr_buffer.getvalue()) + sys.stdout.buffer.write(dill.dumps(script_tuple)) + if exception_raised: + sys.exit(1) + + +def userswitch(func): + @wraps(func) + def dzdo_wrapper(*args, **kwargs): + # jobsubmitter/batchsystem objects will have the user attribute in self + user = args[0].user + if user == getuser(): + return func(args, kwargs) + else: + proc_command = ["dzdo", "-u", f"{user}", sys.executable, "-c", Path(__file__).absolute()] + serialized_func = dill.dumps(func) + func_data = marshal.dumps((serialized_func, args, kwargs)) + try: + dzdo_process = subprocess.run(proc_command, input=func_data, check=True, capture_output=True) + log.error(dzdo_process.stderr) + output, stdout = dill.loads(dzdo_process.stdout) + log.info(stdout) + return output + except subprocess.CalledProcessError as e: + exception_message = f""" + Error while userswitching: + Command: {e.cmd} + Return Code: {e.returncode} + Output: {e.output} + Error: {e.stderr} + """ + raise Exception(exception_message) + except FileNotFoundError as e: + exception_message = f""" + Error, command not found while userswitching: + {e.filename} not found. + """ + raise Exception(exception_message) + + return dzdo_wrapper + + +if __name__ == "__main__": + userscript() From 2d1d39f155d59bbafb085d64b34c44a39e2d702b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Jul 2025 14:11:25 -0400 Subject: [PATCH 110/235] Implement userswitch on user dependent functions --- batch_systems/batch_system.py | 10 ++++--- batch_systems/lsf_client/lsf_client.py | 9 +++++- batch_systems/slurm_client/slurm_client.py | 10 ++++++- orchestrator/tasks.py | 30 ++++++++++++------- submitter/factory.py | 3 ++ submitter/jobsubmitter.py | 29 ++++++++++++++++++ .../nextflow_jobsubmitter.py | 23 +++----------- submitter/toil_submitter/toil_jobsubmitter.py | 23 +++----------- 8 files changed, 83 insertions(+), 54 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index bbf34f21..7ab8376c 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -1,16 +1,17 @@ from django.conf import settings +from getpass import getuser import logging -def get_batch_system(): +def get_batch_system(user=getuser()): if settings.BATCH_SYSTEM == "LSF": from batch_systems.lsf_client.lsf_client import LSFClient - return LSFClient() + return LSFClient(user) elif settings.BATCH_SYSTEM == "SLURM": from batch_systems.slurm_client.slurm_client import SLURMClient - return SLURMClient() + return SLURMClient(user) else: raise Exception(f"Batch system {settings.BATCH_SYSTEM} not supported, please use either LSF or SLURM") @@ -23,13 +24,14 @@ class BatchClient(object): logger (logging): logging module """ - def __init__(self): + def __init__(self, user): """ init function """ self.logger = logging.getLogger("BATCH_client") self.logfileName = "batch.log" self.name = "batch" + self.user = user def submit(self, command, job_args, stdout, job_id, env={}): """ diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index 86980a4a..60c6fc4c 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -11,6 +11,7 @@ from orchestrator.models import Status from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException from batch_systems.batch_system import BatchClient +from submitter.userswitcher import userswitch def format_lsf_job_id(job_id): @@ -25,14 +26,16 @@ class LSFClient(BatchClient): logger (logging): logging module """ - def __init__(self): + def __init__(self, user): """ init function """ self.logger = logging.getLogger("LSF_client") self.logfileName = "lsf.log" self.name = "lsf" + self.user = user + @userswitch def submit(self, command, job_args, stdout, job_id, env={}): """ Submit command to LSF and store log in stdout @@ -72,6 +75,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): ) return self._parse_procid(process.stdout) + @userswitch def terminate(self, job_id): """ Kill LSF job @@ -250,6 +254,7 @@ def _parse_status(self, stdout, external_job_id): return Status.UNKNOWN, error_message.strip() raise FetchStatusException(f"Failed to get status for job {external_job_id}") + @userswitch def status(self, external_job_id): """Parse LSF status @@ -271,6 +276,7 @@ def status(self, external_job_id): status = self._parse_status(process.stdout, external_job_id) return status + @userswitch def suspend(self, job_id): """ Suspend LSF job @@ -286,6 +292,7 @@ def suspend(self, job_id): return True return False + @userswitch def resume(self, job_id): """ Resume LSF job diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 50e4ce02..39fbcde0 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -10,6 +10,7 @@ from orchestrator.models import Status from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException from batch_systems.batch_system import BatchClient +from submitter.userswitcher import userswitch class SLURMClient(BatchClient): @@ -20,14 +21,16 @@ class SLURMClient(BatchClient): logger (logging): logging module """ - def __init__(self): + def __init__(self, user): """ init function """ self.logger = logging.getLogger("SLURM_client") self.logfileName = "slurm.log" self.name = "slurm" + self.user = user + @userswitch def submit(self, command, job_args, stdout, job_id, env={}): """ Submit command to SLURM and store log in stdout @@ -76,6 +79,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): ) return self._parse_procid(process.stdout) + @userswitch def terminate(self, job_id): """ Kill SLURM job @@ -271,6 +275,7 @@ def _parse_status(self, stdout, external_job_id): raise FetchStatusException(f"Failed to get status for job {external_job_id}") + @userswitch def status(self, external_job_id): """Parse SLURM status @@ -286,6 +291,7 @@ def status(self, external_job_id): status = self._parse_status(process.stdout, str(external_job_id)) return status + @userswitch def _get_job_list(self, job_id): """Get slurm job ids in a group @@ -306,6 +312,7 @@ def _get_job_list(self, job_id): slurm_jobs.append(single_slurm_id) return slurm_jobs + @userswitch def suspend(self, job_id): """ Suspend SLURM job @@ -323,6 +330,7 @@ def suspend(self, job_id): return True return False + @userswitch def resume(self, job_id): """ Resume SLURM job diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index dfc769a5..7216125b 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -19,6 +19,7 @@ StopException, FetchStatusException, ) +from submitter.userswitcher import userswitch logger = logging.getLogger(__name__) @@ -33,7 +34,8 @@ def get_job_info_path(job_id): return job_info_path -def save_job_info(job_id, external_id, job_store_location, working_dir, output_directory, metadata={}): +@userswitch +def save_job_info(job, external_id, job_store_location, working_dir, output_directory, metadata={}): if os.path.exists(working_dir): job_info = { "external_id": external_id, @@ -42,7 +44,7 @@ def save_job_info(job_id, external_id, job_store_location, working_dir, output_d "output_directory": output_directory, } job_info.update(metadata) - job_info_path = get_job_info_path(job_id) + job_info_path = get_job_info_path(job.id) with open(job_info_path, "w") as job_info_file: json.dump({"meta": "run_info"}, job_info_file) job_info_file.write("\n") @@ -54,7 +56,7 @@ def save_job_info(job_id, external_id, job_store_location, working_dir, output_d def suspend_job(job): if Status(job.status).transition(Status.SUSPENDED): - job_suspended = get_batch_system().suspend(str(job.id)) + job_suspended = get_batch_system(job.user).suspend(str(job.id)) if not job_suspended: raise RetryException("Failed to suspend job: %s" % str(job.id)) job.update_status(Status.SUSPENDED) @@ -73,6 +75,7 @@ def resume_job(job): log_dir=job.log_dir, log_prefix=job.log_prefix, app_name=job.metadata["pipeline_name"], + user=job.user, ) job_resumed = get_batch_system().resume(submitter.job_id) if not job_resumed: @@ -203,6 +206,7 @@ def prepare_job(job): log_dir=job.log_dir, log_prefix=job.log_prefix, app_name=job.metadata["pipeline_name"], + user=job.user, ) try: job_store_dir, job_work_dir, job_output_dir, log_dir, log_prefix = submitter.prepare_to_submit() @@ -228,10 +232,11 @@ def submit_job_to_batch_system(job, retries=0): log_dir=job.log_dir, log_prefix=job.log_prefix, app_name=job.metadata["pipeline_name"], + user=job.user, ) try: command_line, args, log_path, job_id, env = submitter.get_submit_command() - external_job_id = get_batch_system().submit(command_line, args, log_path, job_id, env) + external_job_id = get_batch_system(job.user).submit(command_line, args, log_path, job_id, env) except Exception as f: if retries < 5: logger.exception(str(f)) @@ -244,7 +249,7 @@ def submit_job_to_batch_system(job, retries=0): job.submitted_to_scheduler(external_job_id) # Keeping this for debugging purposes save_job_info( - str(job.id), + job, external_job_id, submitter.job_store_dir, submitter.job_work_dir, @@ -304,7 +309,7 @@ def check_job_status(job): ): return try: - batch_system_status, batch_system_message = get_batch_system().status(str(job.external_id)) + batch_system_status, batch_system_message = get_batch_system(job.user).status(str(job.external_id)) except FetchStatusException as e: # If failed to check status on batch system retry logger.exception(e) @@ -333,6 +338,7 @@ def check_job_status(job): log_dir=job.log_dir, log_prefix=job.log_prefix, app_name=job.metadata["pipeline_name"], + user=job.user, ) outputs, error_message = submitter.get_outputs() if outputs: @@ -465,12 +471,13 @@ def terminate_job(job): Status.SUSPENDED, Status.UNKNOWN, ): - job_killed = get_batch_system().terminate(str(job.id)) + job_killed = get_batch_system(job.user).terminate(str(job.id)) if not job_killed: raise RetryException("Failed to TERMINATE job %s" % str(job.id)) job.terminate() +@userswitch def set_permission(job): failed_to_set = None dirs = job.root_dir.replace(job.base_dir, "").split("/") @@ -560,15 +567,16 @@ def cleanup_folders(self, job_id, exclude, job_store=True, work_dir=True): logger.error("Job with id:%s not found" % job_id) return if job_store: - if clean_directory(job.job_store_location): + if clean_directory(job, job.job_store_location): job.job_store_clean_up = now() if work_dir: - if clean_directory(job.working_dir, exclude=exclude): + if clean_directory(job, job.working_dir, exclude=exclude): job.working_dir_clean_up = now() job.save() -def clean_directory(path, exclude=[]): +@userswitch +def clean_directory(job, path, exclude=[]): with tempfile.TemporaryDirectory() as tmpdirname: for f in exclude: src = os.path.join(path, f) @@ -617,6 +625,7 @@ def update_command_line_jobs(command_line_jobs, root): ) +@userswitch def check_status_of_command_line_jobs(job): submitter = JobSubmitterFactory.factory( job.type, @@ -628,6 +637,7 @@ def check_status_of_command_line_jobs(job): log_dir=job.log_dir, log_prefix=job.log_prefix, app_name=job.metadata["pipeline_name"], + user=job.user, ) track_cache_str = job.track_cache command_line_status = submitter.get_commandline_status(track_cache_str) diff --git a/submitter/factory.py b/submitter/factory.py index 019696a3..54d69ce8 100644 --- a/submitter/factory.py +++ b/submitter/factory.py @@ -17,6 +17,7 @@ def factory( log_dir=None, log_prefix="", app_name="NA", + user=None, ): if type == PipelineType.CWL: return ToilJobSubmitter( @@ -31,6 +32,7 @@ def factory( log_dir, log_prefix, app_name, + user, ) elif type == PipelineType.NEXTFLOW: return NextflowJobSubmitter( @@ -45,4 +47,5 @@ def factory( log_dir, log_prefix, app_name, + user, ) diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index 4e56bef7..6ca2577b 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -1,5 +1,8 @@ +import os +import shutil from submitter.app import App from django.conf import settings +from submitter.userswitcher import userswitch class JobSubmitter(object): @@ -15,6 +18,7 @@ def __init__( log_prefix="", app_name="NA", root_permissions=settings.OUTPUT_DEFAULT_PERMISSION, + user=None, ): self.app = App.factory(app) self.job_id = job_id @@ -26,6 +30,7 @@ def __init__( self.log_prefix = log_prefix self.app_name = app_name self.root_permissions = root_permissions + self.user = user def prepare_to_submit(self): """ @@ -55,12 +60,36 @@ def _dump_app_inputs(self): :return: app location, inputs, location """ + @userswitch def _prepare_directories(self): """ Prepare execution directories :return: """ + if not os.path.exists(self.job_work_dir): + os.mkdir(self.job_work_dir) + if self.user or self.group: + shutil.chown( + self.job_work_dir, + ) + + if os.path.exists(self.job_store_dir) and not self.resume_jobstore: + + shutil.rmtree(self.job_store_dir) + + if self.resume_jobstore: + if not os.path.exists(self.resume_jobstore): + raise Exception("The jobstore indicated to be resumed could not be found") + + if not os.path.exists(self.job_tmp_dir): + os.mkdir(self.job_tmp_dir) + + if self.log_dir: + if not os.path.exists(self.log_dir): + mode_int = int(self.root_permissions, 8) + os.makedirs(self.log_dir, mode=mode_int, exist_ok=True) + def _job_args(self): pass diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 7309d109..09c71bae 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -5,6 +5,7 @@ from django.conf import settings from submitter import JobSubmitter from batch_systems.batch_system import get_batch_system +from submitter.userswitcher import userswitch class NextflowJobSubmitter(JobSubmitter): @@ -22,6 +23,7 @@ def __init__( log_prefix="", app_name="NA", root_permissions=settings.OUTPUT_DEFAULT_PERMISSION, + user=None, ): """ :param job_id: @@ -60,6 +62,7 @@ def __init__( log_prefix, app_name, root_permissions, + user, ) self.resume_jobstore = resume_jobstore dir_config = settings.PIPELINE_CONFIG.get(self.app_name) @@ -207,6 +210,7 @@ def inputs_location(self): def config_location(self): return os.path.join(self.job_work_dir, "nf.config") + @userswitch def _dump_app_inputs(self): input_map = dict() inputs = self.inputs.get("inputs", []) @@ -229,25 +233,6 @@ def _dump_config(self, config): f.write(config) return file_path - def _prepare_directories(self): - if not os.path.exists(self.job_work_dir): - os.mkdir(self.job_work_dir) - - if os.path.exists(self.job_store_dir) and not self.resume_jobstore: - shutil.rmtree(self.job_store_dir) - - if self.resume_jobstore: - if not os.path.exists(self.resume_jobstore): - raise Exception("The jobstore indicated to be resumed could not be found") - - if not os.path.exists(self.job_tmp_dir): - os.mkdir(self.job_tmp_dir) - - if self.log_dir: - if not os.path.exists(self.log_dir): - mode_int = int(self.root_permissions, 8) - os.makedirs(self.log_dir, mode=mode_int, exist_ok=True) - def _command_line(self): profile = self.inputs["profile"] params = self.inputs.get("params", {}) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 455adc56..3d41e002 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -8,6 +8,7 @@ from submitter import JobSubmitter from .toil_track_utils import ToilTrack, ToolStatus from batch_systems.batch_system import get_batch_system +from submitter.userswitcher import userswitch def translate_toil_to_model_status(status): @@ -39,6 +40,7 @@ def __init__( log_prefix="", app_name="NA", root_permissions=settings.OUTPUT_DEFAULT_PERMISSION, + user=None, ): JobSubmitter.__init__( self, @@ -52,6 +54,7 @@ def __init__( log_prefix, app_name, root_permissions, + user, ) dir_config = settings.PIPELINE_CONFIG.get(self.app_name) if not dir_config: @@ -179,6 +182,7 @@ def app_location(self): def inputs_location(self): return os.path.join(self.job_work_dir, "input.json") + @userswitch def _dump_app_inputs(self): inputs_location = self.inputs_location with open(inputs_location, "w") as f: @@ -189,25 +193,6 @@ def _dump_app_inputs(self): with open(inputs_log_location, "w") as f: json.dump(self.inputs, f) - def _prepare_directories(self): - if not os.path.exists(self.job_work_dir): - os.mkdir(self.job_work_dir) - - if self.log_dir: - if not os.path.exists(self.log_dir): - mode_int = int(self.root_permissions, 8) - os.makedirs(self.log_dir, mode=mode_int, exist_ok=True) - - if os.path.exists(self.job_store_dir) and not self.resume_jobstore: - shutil.rmtree(self.job_store_dir) - - if self.resume_jobstore: - if not os.path.exists(self.resume_jobstore): - raise Exception("The job_store indicated to be resumed could not be found") - - if not os.path.exists(self.job_tmp_dir): - os.mkdir(self.job_tmp_dir) - def _leader_args(self): single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) args = self._walltime() From 8be3f47635a41723444d198e4f83364fb8c69708 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Jul 2025 14:29:29 -0400 Subject: [PATCH 111/235] Added batch system specific function for env export --- batch_systems/batch_system.py | 8 ++++++++ batch_systems/lsf_client/lsf_client.py | 9 +++++++++ batch_systems/slurm_client/slurm_client.py | 9 +++++++++ 3 files changed, 26 insertions(+) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index bbf34f21..52e073e0 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -78,6 +78,14 @@ def set_num_tasks(self, num_tasks, default=None): num_task_args = [] return num_task_args + def get_env_export_flag(self): + """ + Flag to enable env propagation for the batch jobs + + Returns: + str: CLI flag to enable env propagation + """ + def set_group(self, group_id): """ Set the group args of the batch job diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index 86980a4a..da607f73 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -116,6 +116,15 @@ def set_num_tasks(self, num_tasks, default=None): num_task_args = ["-n", num_tasks] return num_task_args + def get_env_export_flag(self): + """ + Flag to enable env propagation for the batch jobs + + Returns: + str: CLI flag to enable env propagation + """ + return "-env all" + def set_group(self, group_id): group_id_args = [] if group_id: diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 50e4ce02..0691d8e8 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -122,6 +122,15 @@ def set_num_tasks(self, num_tasks, default=None): num_task_args = [f"--cpus-per-task={num_tasks}"] return num_task_args + def get_env_export_flag(self): + """ + Flag to enable env propagation for the batch jobs + + Returns: + str: CLI flag to enable env propagation + """ + return "--export=ALL" + def set_group(self, group_id): group_id_args = [] if group_id: From a4525e62ca47807dca73354dbe8efb8cc557eb83 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 30 Jul 2025 14:30:00 -0400 Subject: [PATCH 112/235] Add env export to ACCESS legacy --- submitter/toil_submitter/toil_jobsubmitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 607733a9..75e23522 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -91,7 +91,7 @@ def get_submit_command(self): env[self.batch_system_args_env] = toil_batch_system_args.strip() if settings.ACCESS_LEGACY_APP in self.app.github.lower(): env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) - env[self.batch_system_args_env] += "--export=ALL" + env[self.batch_system_args_env] += self.batch_system.get_env_export_flag() return command_line, self._leader_args(), log_path, self.job_id, env def get_commandline_status(self, cache): From 46a8b89f16020befc250583873120a3300507d1b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:24:36 -0400 Subject: [PATCH 113/235] Make user an optional parameter --- batch_systems/batch_system.py | 2 +- batch_systems/lsf_client/lsf_client.py | 3 ++- batch_systems/slurm_client/slurm_client.py | 3 ++- submitter/factory.py | 3 ++- submitter/jobsubmitter.py | 3 ++- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 4 ++-- submitter/toil_submitter/toil_jobsubmitter.py | 4 ++-- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index 09b7e1aa..6488aeff 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -24,7 +24,7 @@ class BatchClient(object): logger (logging): logging module """ - def __init__(self, user): + def __init__(self, user=getuser()): """ init function """ diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index e2bfb440..176caf49 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -12,6 +12,7 @@ from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException from batch_systems.batch_system import BatchClient from submitter.userswitcher import userswitch +from getpass import getuser def format_lsf_job_id(job_id): @@ -26,7 +27,7 @@ class LSFClient(BatchClient): logger (logging): logging module """ - def __init__(self, user): + def __init__(self, user=getuser()): """ init function """ diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 02babbc6..dbe6c7af 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -11,6 +11,7 @@ from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException from batch_systems.batch_system import BatchClient from submitter.userswitcher import userswitch +from getpass import getuser class SLURMClient(BatchClient): @@ -21,7 +22,7 @@ class SLURMClient(BatchClient): logger (logging): logging module """ - def __init__(self, user): + def __init__(self, user=getuser()): """ init function """ diff --git a/submitter/factory.py b/submitter/factory.py index 54d69ce8..e01c95d8 100644 --- a/submitter/factory.py +++ b/submitter/factory.py @@ -1,5 +1,6 @@ from orchestrator.models import PipelineType from submitter import NextflowJobSubmitter, ToilJobSubmitter +from getpass import getuser class JobSubmitterFactory(object): @@ -17,7 +18,7 @@ def factory( log_dir=None, log_prefix="", app_name="NA", - user=None, + user=getuser(), ): if type == PipelineType.CWL: return ToilJobSubmitter( diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index 6ca2577b..bf7c42af 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -3,6 +3,7 @@ from submitter.app import App from django.conf import settings from submitter.userswitcher import userswitch +from getpass import getuser class JobSubmitter(object): @@ -18,7 +19,7 @@ def __init__( log_prefix="", app_name="NA", root_permissions=settings.OUTPUT_DEFAULT_PERMISSION, - user=None, + user=getuser(), ): self.app = App.factory(app) self.job_id = job_id diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 09c71bae..87e26225 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -1,11 +1,11 @@ import os -import shutil import hashlib import json from django.conf import settings from submitter import JobSubmitter from batch_systems.batch_system import get_batch_system from submitter.userswitcher import userswitch +from getpass import getuser class NextflowJobSubmitter(JobSubmitter): @@ -23,7 +23,7 @@ def __init__( log_prefix="", app_name="NA", root_permissions=settings.OUTPUT_DEFAULT_PERMISSION, - user=None, + user=getuser(), ): """ :param job_id: diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index eac47586..38e2584e 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -1,6 +1,5 @@ import os import json -import shutil import copy from django.conf import settings from django.core.serializers.json import DjangoJSONEncoder @@ -9,6 +8,7 @@ from .toil_track_utils import ToilTrack, ToolStatus from batch_systems.batch_system import get_batch_system from submitter.userswitcher import userswitch +from getpass import getuser def translate_toil_to_model_status(status): @@ -40,7 +40,7 @@ def __init__( log_prefix="", app_name="NA", root_permissions=settings.OUTPUT_DEFAULT_PERMISSION, - user=None, + user=getuser(), ): JobSubmitter.__init__( self, From fcba40718b557c7738c3a4059c1d7b0cb3758fae Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:25:23 -0400 Subject: [PATCH 114/235] Make sure resume is using the right user --- orchestrator/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 7216125b..900c1f23 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -77,7 +77,7 @@ def resume_job(job): app_name=job.metadata["pipeline_name"], user=job.user, ) - job_resumed = get_batch_system().resume(submitter.job_id) + job_resumed = get_batch_system(job.user).resume(submitter.job_id) if not job_resumed: raise RetryException("Failed to resume job: %s" % str(job.id)) job.update_status(Status.RUNNING) From 45265eba5e14d5e248e2a932008aaad5dfa11e9e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:31:07 -0400 Subject: [PATCH 115/235] Fix flake test --- submitter/userswitcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index fb99d974..5ff25c67 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -21,7 +21,7 @@ def userscript(): func_serialized, args, kwargs = marshal.loads(sys.stdin.buffer.read()) func = dill.loads(func_serialized) output = func(*args, **kwargs) - except Exception as e: + except Exception: log.exception("Exception when running the function as another user") exception_raised = True script_tuple = (output, stdout_buffer.getvalue()) From 1cf05d684d226f207c7543f515732912fe1c8bb4 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:31:39 -0400 Subject: [PATCH 116/235] Fix test using the default user --- submitter/userswitcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 5ff25c67..5f233a1a 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -37,7 +37,7 @@ def dzdo_wrapper(*args, **kwargs): # jobsubmitter/batchsystem objects will have the user attribute in self user = args[0].user if user == getuser(): - return func(args, kwargs) + return func(*args, **kwargs) else: proc_command = ["dzdo", "-u", f"{user}", sys.executable, "-c", Path(__file__).absolute()] serialized_func = dill.dumps(func) From c6a4b068bc2c09dccf1f2f1e21a47fe7b3403126 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:32:31 -0400 Subject: [PATCH 117/235] Add user and groups to the image --- compose.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compose.yaml b/compose.yaml index f66b235a..3ead1388 100644 --- a/compose.yaml +++ b/compose.yaml @@ -11,6 +11,18 @@ x-ridgeback_celery: - ${DOCKER_GID} - ${GID_FILE_1} - ${GID_FILE_2} + post_start: + - command: + - /bin/bash + - -c + - | + for single_group in "${all_groups[@]}"; do + echo "groupadd -g ${!single_group[0]} ${!single_group[1]}" + done + for single_user in "${all_users[@]}"; do + echo "useradd --uid ${!single_user[0]} -m ${!single_user[1]} -G ${!single_user[2]}" + done + user: root env_file: .env environment: - RIDGEBACK_DB_URL=ridgeback_postgres From 4330672454566637aa2bdf9ed1d0b564fb79608c Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:32:59 -0400 Subject: [PATCH 118/235] Connect to centrify in the docker container --- compose.yaml | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/compose.yaml b/compose.yaml index 3ead1388..09a64a95 100644 --- a/compose.yaml +++ b/compose.yaml @@ -48,10 +48,6 @@ x-ridgeback_celery: source: ${SLURM_LIB_PATH} target: ${SLURM_LIB_PATH} read_only: true - - type: bind - source: ${SLURM_ETC_PASSWD} - target: ${SLURM_ETC_PASSWD} - read_only: true - type: bind source: ${SLURM_MUNGE_VAR} target: ${SLURM_MUNGE_VAR} @@ -64,7 +60,39 @@ x-ridgeback_celery: source: ${CLUSTER_ADMIN_MOUNT} target: ${CLUSTER_ADMIN_MOUNT} read_only: true - entrypoint: ["/bin/bash","-c"] + - type: bind + source: ${USER_SOFTWARE_MOUNT} + target: ${USER_SOFTWARE_MOUNT} + read_only: true + - type: bind + source: ${CENTRIFYDC_PATH} + target: ${CENTRIFYDC_PATH} + read_only: true + - type: bind + source: ${CENTRIFYDC_DZDO_PATH} + target: ${CENTRIFYDC_DZDO_PATH} + read_only: true + - type: bind + source: ${CENTRIFYDC_ADINFO_PATH} + target: ${CENTRIFYDC_ADINFO_PATH} + read_only: true + - type: bind + source: ${CENTRIFYDC_ETC_PATH} + target: ${CENTRIFYDC_ETC_PATH} + read_only: true + - type: bind + source: ${CENTRIFYDC_VAR_PATH} + target: ${CENTRIFYDC_VAR_PATH} + read_only: true + - type: bind + source: ${CENTRIFYDC_NSSWITCH_PATH} + target: ${CENTRIFYDC_NSSWITCH_PATH} + read_only: true + - type: bind + source: ${CENTRIFYDC_LIBNSS_PATH} + target: ${CENTRIFYDC_LIBNSS_PATH} + read_only: true + entrypoint: ["/bin/bash", "-c"] healthcheck: test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status || exit 1" interval: 30s From 591c36ae47c51af7dba17cb9f050542fe8c12f1d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:33:50 -0400 Subject: [PATCH 119/235] Formatting fixes --- compose.yaml | 222 ++++++++++++++++++++++++++------------------------- 1 file changed, 115 insertions(+), 107 deletions(-) diff --git a/compose.yaml b/compose.yaml index 09a64a95..30097400 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,8 +1,7 @@ -name: 'Ridgeback Services' +name: "Ridgeback Services" -x-ridgeback_celery: - &ridgeback_celery - image: mskcc/ridgeback:${RIDGEBACK_VERSION} +x-ridgeback_celery: &ridgeback_celery + image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" networks: @@ -126,7 +125,7 @@ services: - ${DB_BACKUP_PATH}:/db_backup entrypoint: ["/bin/sh", "-c"] command: - - | + - | chown -R ${DOCKER_UID}:${DOCKER_GID} /postgres chown -R ${DOCKER_UID}:${DOCKER_GID} /logs chown -R ${DOCKER_UID}:${DOCKER_GID} /celery @@ -184,7 +183,11 @@ services: - -c - max_parallel_maintenance_workers=4 healthcheck: - test: ["CMD-SHELL", "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'"] + test: + [ + "CMD-SHELL", + "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'", + ] interval: 30s timeout: 3s retries: 3 @@ -229,7 +232,7 @@ services: depends_on: - ridgeback_create_volumes ridgeback: - image: mskcc/ridgeback:${RIDGEBACK_VERSION} + image: mskcc/ridgeback:${RIDGEBACK_VERSION} restart: always user: "${DOCKER_UID}:${DOCKER_GID}" networks: @@ -248,15 +251,16 @@ services: - ./server/:/ridgeback_staticfiles/ ports: - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} - entrypoint: ["/bin/bash","-c"] + entrypoint: ["/bin/bash", "-c"] command: - | - python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput - echo "User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin','voyager@mskcc.org','${RIDGEBACK_DB_PASSWORD}')" | python3 ${RIDGEBACK_PATH}/manage.py shell_plus - python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput - python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_PORT} >> /ridgeback/server/web_server.log 2>&1 + python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput + echo "User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin','voyager@mskcc.org','${RIDGEBACK_DB_PASSWORD}')" | python3 ${RIDGEBACK_PATH}/manage.py shell_plus + python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput + python3 ${RIDGEBACK_PATH}/manage.py runserver 0.0.0.0:${RIDGEBACK_PORT} >> /ridgeback/server/web_server.log 2>&1 healthcheck: - test: ["CMD-SHELL", "curl -sSf http://localhost:${RIDGEBACK_PORT}/ || exit 1"] + test: + ["CMD-SHELL", "curl -sSf http://localhost:${RIDGEBACK_PORT}/ || exit 1"] interval: 30s timeout: 3s retries: 3 @@ -274,29 +278,33 @@ services: <<: *ridgeback_celery command: - | - if ! python3 -c "import toil" 2>&1 >/dev/null - then - pip3 install --upgrade pip && - pip3 install --force-reinstall 'setuptools<58.0.0' && - pip3 install "cython<3.0.0" wheel && - pip3 install "pyyaml==5.4.1" --no-build-isolation && - pip3 install -r ${RIDGEBACK_PATH}/requirements.txt && - pip3 install -r ${RIDGEBACK_PATH}/requirements-toil.txt - fi - - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid + if ! python3 -c "import toil" 2>&1 >/dev/null + then + pip3 install --upgrade pip && + pip3 install --force-reinstall 'setuptools<58.0.0' && + pip3 install "cython<3.0.0" wheel && + pip3 install "pyyaml==5.4.1" --no-build-isolation && + pip3 install -r ${RIDGEBACK_PATH}/requirements.txt && + pip3 install -r ${RIDGEBACK_PATH}/requirements-toil.txt + fi + + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running orchestrator beat...' + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running orchestrator beat...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator beat \ - -l info \ - -f /ridgeback/celery/logs/ridgeback_beat.log \ - --pidfile $$PIDFILE \ - -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator beat \ + -l info \ + -f /ridgeback/celery/logs/ridgeback_beat.log \ + --pidfile $$PIDFILE \ + -s /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.celerybeat-schedule healthcheck: - test: ["CMD-SHELL", "ps -p $(pgrep -F /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid)"] + test: + [ + "CMD-SHELL", + "ps -p $(pgrep -F /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid)", + ] interval: 30s timeout: 3s retries: 3 @@ -314,105 +322,105 @@ services: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running command queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running command queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_COMMAND_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=30 \ - -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_COMMAND_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=30 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} ridgeback_celery_action_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running action queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_ACTION_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=10 \ - -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running action queue worker...' + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_ACTION_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} ridgeback_celery_check_status_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running check status queue worker...' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=10 + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running check status queue worker...' + + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 ridgeback_celery_submit_job_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate + + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running submit job queue worker...' - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running submit job queue worker...' - - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=5 + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=5 ridgeback_celery_set_permission_queue: <<: *ridgeback_celery command: - - | - source ${RIDGEBACK_VENV}/bin/activate + - | + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running set permission queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running set permission queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=10 + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=10 ridgeback_celery_cleanup_queue: <<: *ridgeback_celery command: - | - source ${RIDGEBACK_VENV}/bin/activate + source ${RIDGEBACK_VENV}/bin/activate - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid - [ -e $$PIDFILE ] && rm $$PIDFILE - echo 'Running cleanup queue worker...' + PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE}.pid + [ -e $$PIDFILE ] && rm $$PIDFILE + echo 'Running cleanup queue worker...' - celery --workdir ${RIDGEBACK_PATH} \ - -A orchestrator worker \ - -l info \ - -Q ${RIDGEBACK_CLEANUP_QUEUE} \ - -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ - --pidfile $$PIDFILE \ - --concurrency=2 + celery --workdir ${RIDGEBACK_PATH} \ + -A orchestrator worker \ + -l info \ + -Q ${RIDGEBACK_CLEANUP_QUEUE} \ + -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ + --pidfile $$PIDFILE \ + --concurrency=2 ridgeback_logrotate: image: mskcc/voyager-compose-utils:1.0.0 restart: always @@ -424,7 +432,7 @@ services: - ./logrotate/:/logrotate entrypoint: ["/bin/sh", "-c"] command: - - | + - | mkdir -p /logs/archive cat > /logrotate/logrotate.conf < /db_backup/db_backup.cron < Date: Thu, 31 Jul 2025 12:38:41 -0400 Subject: [PATCH 120/235] Add collab and test mounts --- compose.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compose.yaml b/compose.yaml index 30097400..5a75f224 100644 --- a/compose.yaml +++ b/compose.yaml @@ -39,6 +39,8 @@ x-ridgeback_celery: &ridgeback_celery - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - ${CLUSTER_SCRATCH_MOUNT}:${CLUSTER_SCRATCH_MOUNT} - ${CLUSTER_FILESYSTEM_SHARE_MOUNT}:${CLUSTER_FILESYSTEM_SHARE_MOUNT} + - ${CLUSTER_FILESYSTEM_COLLAB}:${CLUSTER_FILESYSTEM_COLLAB} + - ${CLUSTER_FILESYSTEM_TEST}:${CLUSTER_FILESYSTEM_TEST} - type: bind source: ${SLURM_ETC} target: ${SLURM_ETC} From 1d01cb447ab4b8f3abc7516c283710a79902cb81 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 12:58:51 -0400 Subject: [PATCH 121/235] Add userswitch settings --- ridgeback/settings.py | 1 + submitter/userswitcher.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 77836d99..c9fc76ce 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -190,6 +190,7 @@ RABBITMQ_USERNAME = os.environ.get("RIDGEBACK_RABBITMQ_USERNAME", "guest") RABBITMQ_PASSWORD = os.environ.get("RIDGEBACK_RABBITMQ_PASSWORD", "guest") RABBITMQ_URL = os.environ.get("RIDGEBACK_RABBITMQ_URL", "localhost") +ENABLE_USER_SWITCH = True if os.environ.get("RIDGEBACK_ENABLE_USER_SWITCH", "True") != "False" else False CELERY_BROKER_URL = os.environ.get( "CELERY_BROKER_URL", diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 5f233a1a..b5445e4d 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -8,6 +8,7 @@ from pathlib import Path from functools import wraps from getpass import getuser +from django.conf import settings log = logging.getLogger(__name__) @@ -36,7 +37,7 @@ def userswitch(func): def dzdo_wrapper(*args, **kwargs): # jobsubmitter/batchsystem objects will have the user attribute in self user = args[0].user - if user == getuser(): + if user == getuser() or not settings.ENABLE_USER_SWITCH: return func(*args, **kwargs) else: proc_command = ["dzdo", "-u", f"{user}", sys.executable, "-c", Path(__file__).absolute()] From 2db3c4a2ffa536144f068363c65fc8287f8530f5 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 14:22:06 -0400 Subject: [PATCH 122/235] Added black formatting --- orchestrator/migrations/0021_job_user.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/orchestrator/migrations/0021_job_user.py b/orchestrator/migrations/0021_job_user.py index 5820092c..94373434 100644 --- a/orchestrator/migrations/0021_job_user.py +++ b/orchestrator/migrations/0021_job_user.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('orchestrator', '0020_auto_20250402_0942'), + ("orchestrator", "0020_auto_20250402_0942"), ] operations = [ migrations.AddField( - model_name='job', - name='user', - field=models.CharField(default='kumarn1', max_length=100), + model_name="job", + name="user", + field=models.CharField(default="kumarn1", max_length=100), ), ] From c9f35dde0adeaa1f88b354b164e0dcf5e0ebede7 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 31 Jul 2025 19:09:51 -0400 Subject: [PATCH 123/235] Remove group add --- compose.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compose.yaml b/compose.yaml index 5a75f224..86d780bf 100644 --- a/compose.yaml +++ b/compose.yaml @@ -6,10 +6,6 @@ x-ridgeback_celery: &ridgeback_celery user: "${DOCKER_UID}:${DOCKER_GID}" networks: - voyager_net - group_add: - - ${DOCKER_GID} - - ${GID_FILE_1} - - ${GID_FILE_2} post_start: - command: - /bin/bash From defc0dff69d4f91d0969be460617ab259f1c275f Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 5 Aug 2025 14:04:56 -0400 Subject: [PATCH 124/235] Add function for defaults handling to prevent schema changes --- orchestrator/models.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/orchestrator/models.py b/orchestrator/models.py index 6eacce1a..0afb02a7 100755 --- a/orchestrator/models.py +++ b/orchestrator/models.py @@ -23,6 +23,15 @@ def message_default(): return message_default_dict +def get_default_for_job(model_field): + if model_field == "root_permission": + return settings.OUTPUT_DEFAULT_PERMISSION + elif model_field == "output_uid": + return settings.OUTPUT_DEFAULT_UID + elif model_field == "output_gid": + return settings.OUTPUT_DEFAULT_GID + + class Status(IntEnum): CREATED = 0 PREPARED = 1 @@ -167,10 +176,10 @@ class Job(BaseModel): external_id = models.CharField(max_length=50, null=True, blank=True) base_dir = models.CharField(max_length=1000) root_dir = models.CharField(max_length=1000) - root_permission = models.CharField(default=settings.OUTPUT_DEFAULT_PERMISSION, max_length=3) + root_permission = models.CharField(default=get_default_for_job("root_permission"), max_length=3) user = models.CharField(default=getuser(), max_length=100) - output_uid = models.IntegerField(default=settings.OUTPUT_DEFAULT_UID, editable=True) - output_gid = models.IntegerField(default=settings.OUTPUT_DEFAULT_GID, editable=True) + output_uid = models.IntegerField(default=get_default_for_job("output_gid"), editable=True) + output_gid = models.IntegerField(default=get_default_for_job("output_gid"), editable=True) job_store_location = models.CharField(max_length=1000, null=True, blank=True) resume_job_store_location = models.CharField(max_length=1000, null=True, blank=True) working_dir = models.CharField(max_length=1000, null=True, blank=True) From efa28be1e08e30b53240dba377dd14dc9f72ce8e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 5 Aug 2025 14:06:31 -0400 Subject: [PATCH 125/235] Added migration file --- .../migrations/0022_auto_20250805_1406.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 orchestrator/migrations/0022_auto_20250805_1406.py diff --git a/orchestrator/migrations/0022_auto_20250805_1406.py b/orchestrator/migrations/0022_auto_20250805_1406.py new file mode 100644 index 00000000..bd432693 --- /dev/null +++ b/orchestrator/migrations/0022_auto_20250805_1406.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2025-08-05 18:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("orchestrator", "0021_job_user"), + ] + + operations = [ + migrations.AlterField( + model_name="job", + name="output_uid", + field=models.IntegerField(default=6146), + ), + ] From 559a082c47d7f62bc77266935e4e128cb1394540 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 5 Aug 2025 14:09:18 -0400 Subject: [PATCH 126/235] Update user/group creation in docker --- compose.yaml | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/compose.yaml b/compose.yaml index 86d780bf..5b76ba02 100644 --- a/compose.yaml +++ b/compose.yaml @@ -11,14 +11,27 @@ x-ridgeback_celery: &ridgeback_celery - /bin/bash - -c - | - for single_group in "${all_groups[@]}"; do - echo "groupadd -g ${!single_group[0]} ${!single_group[1]}" + sed -i 's/UID_MAX 60000/UID_MAX 6000000000/g' /etc/login.defs + sed -i 's/GID_MAX 60000/GID_MAX 6000000000/g' /etc/login.defs + sed -i 's/UID_MIN 1000/UID_MIN 100/g' /etc/login.defs + sed -i 's/GID_MIN 1000/GID_MIN 100/g' /etc/login.defs + getent group slurm &>/dev/null || groupadd -g ${SLURM_UID} slurm + getent group munge &>/dev/null || groupadd -g ${MUNGE_UID} munge + for single_group in ${all_groups}; do + single_group_items=($${!single_group[0]}) + getent group $${single_group_items[1]} &>/dev/null || groupadd -g $${single_group_items[0]} $${single_group_items[1]} done - for single_user in "${all_users[@]}"; do - echo "useradd --uid ${!single_user[0]} -m ${!single_user[1]} -G ${!single_user[2]}" + id -u ${DOCKER_USERNAME} &>/dev/null || useradd -s /bin/bash -d ${HOME} --uid ${DOCKER_UID} -m ${DOCKER_USERNAME} -G ${DOCKER_GROUPS} + id -u slurm &>/dev/null || useradd -s /bin/bash -d /var/lib/slurm --uid ${SLURM_UID} -m slurm -g slurm + id -u munge &>/dev/null || useradd -s /sbin/nologin -d ${SLURM_MUNGE_VAR} --uid ${MUNGE_UID} -m munge -g munge + for single_user in ${all_users}; do + single_user_items=($${!single_user[0]}) + id -u $${single_user_items[1]} &>/dev/null || useradd -s /bin/bash --uid $${single_user_items[0]} -m $${single_user_items[1]} -G $${single_user_items[2]} done user: root env_file: .env + group_add: + - ${DOCKER_GROUPS} environment: - RIDGEBACK_DB_URL=ridgeback_postgres - RIDGEBACK_MEMCACHED_HOST=ridgeback_memcached @@ -236,6 +249,8 @@ services: networks: - voyager_net env_file: .env + group_add: + - ${DOCKER_GROUPS} environment: - RIDGEBACK_DB_URL=ridgeback_postgres - RIDGEBACK_MEMCACHED_HOST=ridgeback_memcached @@ -249,6 +264,21 @@ services: - ./server/:/ridgeback_staticfiles/ ports: - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} + post_start: + - command: + - /bin/bash + - -c + - | + sed -i 's/UID_MAX 60000/UID_MAX 6000000000/g' /etc/login.defs + sed -i 's/GID_MAX 60000/GID_MAX 6000000000/g' /etc/login.defs + sed -i 's/UID_MIN 1000/UID_MIN 100/g' /etc/login.defs + sed -i 's/GID_MIN 1000/GID_MIN 100/g' /etc/login.defs + for single_group in ${all_groups}; do + single_group_items=($${!single_group[0]}) + getent group $${single_group_items[1]} &>/dev/null || groupadd -g $${single_group_items[0]} $${single_group_items[1]} + done + id -u ${DOCKER_USERNAME} &>/dev/null || useradd -s /bin/bash -d ${HOME} --uid ${DOCKER_UID} -m ${DOCKER_USERNAME} -G ${DOCKER_GROUPS} + user: root entrypoint: ["/bin/bash", "-c"] command: - | From dc06f735867edb89fbcd68b80854e9f1ff12ad4c Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 5 Aug 2025 14:09:38 -0400 Subject: [PATCH 127/235] Fixed bug on env handling --- compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index 5b76ba02..0b687598 100644 --- a/compose.yaml +++ b/compose.yaml @@ -495,7 +495,7 @@ services: - ./logs:/logs - ${DB_BACKUP_PATH}:/db_backup environment: - - PGPASSWORD=${RIDGEBACK_DB_USERNAME} + - PGPASSWORD=${RIDGEBACK_DB_PASSWORD} entrypoint: ["/bin/sh", "-c"] command: - | From 8fa32b7a5df6b9fdaaa5c76230e967bbd50faaa1 Mon Sep 17 00:00:00 2001 From: buehlere Date: Thu, 7 Aug 2025 15:00:01 -0400 Subject: [PATCH 128/235] Update test_tasks.py --- tests/test_tasks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_tasks.py b/tests/test_tasks.py index b6f6440b..a3adb9d2 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -282,9 +282,9 @@ def test_job_args_all_options_slurm(self): app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} root_dir = "test_root" resume_jobstore = None - walltime = 7200 - tool_walltime = 24 - walltime_hard = 24 + walltime = "2:00:00" + tool_walltime = "1-00:00:00" + walltime_hard = "1-00:00:00" memlimit = 10 inputs = {} expected_leader_args = "--time={} --mem={}G".format(walltime, memlimit) From efec79111b9a102d4fdab84a944bf87e71a7110b Mon Sep 17 00:00:00 2001 From: buehlere Date: Thu, 7 Aug 2025 16:01:45 -0400 Subject: [PATCH 129/235] fixing batch system statement --- submitter/toil_submitter/toil_jobsubmitter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 75e23522..6fc9097e 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -91,7 +91,7 @@ def get_submit_command(self): env[self.batch_system_args_env] = toil_batch_system_args.strip() if settings.ACCESS_LEGACY_APP in self.app.github.lower(): env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) - env[self.batch_system_args_env] += self.batch_system.get_env_export_flag() + env[self.batch_system_args_env] = " ".join([env[self.batch_system_args_env], self.batch_system.get_env_export_flag()]) return command_line, self._leader_args(), log_path, self.job_id, env def get_commandline_status(self, cache): From 6b154b71007bc867a08ea4506cd25d5ba6cccff6 Mon Sep 17 00:00:00 2001 From: buehlere Date: Thu, 7 Aug 2025 16:07:56 -0400 Subject: [PATCH 130/235] Revert "Update test_tasks.py" This reverts commit 8fa32b7a5df6b9fdaaa5c76230e967bbd50faaa1. --- tests/test_tasks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_tasks.py b/tests/test_tasks.py index a3adb9d2..b6f6440b 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -282,9 +282,9 @@ def test_job_args_all_options_slurm(self): app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} root_dir = "test_root" resume_jobstore = None - walltime = "2:00:00" - tool_walltime = "1-00:00:00" - walltime_hard = "1-00:00:00" + walltime = 7200 + tool_walltime = 24 + walltime_hard = 24 memlimit = 10 inputs = {} expected_leader_args = "--time={} --mem={}G".format(walltime, memlimit) From ea30ae4d997871ba98859f9ba95ac6957478f658 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 8 Aug 2025 10:20:40 -0400 Subject: [PATCH 131/235] Added black formatting --- submitter/toil_submitter/toil_jobsubmitter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 6fc9097e..3e811f7f 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -91,7 +91,9 @@ def get_submit_command(self): env[self.batch_system_args_env] = toil_batch_system_args.strip() if settings.ACCESS_LEGACY_APP in self.app.github.lower(): env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) - env[self.batch_system_args_env] = " ".join([env[self.batch_system_args_env], self.batch_system.get_env_export_flag()]) + env[self.batch_system_args_env] = " ".join( + [env[self.batch_system_args_env], self.batch_system.get_env_export_flag()] + ) return command_line, self._leader_args(), log_path, self.job_id, env def get_commandline_status(self, cache): From d7d70f0557037a2399111b66d28fdda4a65c2200 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:34:08 -0400 Subject: [PATCH 132/235] Added ipython --- requirements.txt | 1 + ridgeback/settings.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index d93b6a02..083cd3b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -19,3 +19,4 @@ black==22.3.0 ddtrace==1.7.3 django-extensions==3.1.1 urllib3==1.26.6 +ipython==8.37.0 diff --git a/ridgeback/settings.py b/ridgeback/settings.py index c9fc76ce..fd8fe03c 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -321,3 +321,5 @@ ACCESS_LEGACY_CONDA_ENV = os.environ.get( "ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin" ) + +# SHELL_PLUS = "ipython" \ No newline at end of file From e60137b435166c3d3411e2dfa7bbdbaf8b04767b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:35:10 -0400 Subject: [PATCH 133/235] Fix app resolution per user --- submitter/toil_submitter/toil_jobsubmitter.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 38e2584e..92361fbb 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -53,8 +53,8 @@ def __init__( log_dir, log_prefix, app_name, - root_permissions, - user, + root_permissions=root_permissions, + user=user, ) dir_config = settings.PIPELINE_CONFIG.get(self.app_name) if not dir_config: @@ -78,9 +78,13 @@ def __init__( def prepare_to_submit(self): self._prepare_directories() self._dump_app_inputs() - self.app.resolve(self.job_work_dir) + self.resolve_app() return self.job_store_dir, self.job_work_dir, self.job_outputs_dir, self.log_dir, self.log_prefix + @userswitch + def resolve_app(self): + self.app.resolve(self.job_work_dir) + def get_submit_command(self): command_line = self._command_line() log_path = os.path.join(self.job_work_dir, self.batch_system.logfileName) From b43a282764e92f946b267a6f0ef1eb5fb56f7818 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:35:26 -0400 Subject: [PATCH 134/235] Fix batch system env handling --- submitter/toil_submitter/toil_jobsubmitter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 92361fbb..45345a46 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -98,7 +98,9 @@ def get_submit_command(self): env[self.batch_system_args_env] = toil_batch_system_args.strip() if settings.ACCESS_LEGACY_APP in self.app.github.lower(): env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) - env[self.batch_system_args_env] += self.batch_system.get_env_export_flag() + env[self.batch_system_args_env] = " ".join( + [env[self.batch_system_args_env], self.batch_system.get_env_export_flag()] + ) return command_line, self._leader_args(), log_path, self.job_id, env def get_commandline_status(self, cache): From 0593f4a0c0dba29a4ff8ab926611b26a549cdba0 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:36:37 -0400 Subject: [PATCH 135/235] Ensure user information on parent objects --- orchestrator/tasks.py | 6 +++--- submitter/factory.py | 4 ++-- submitter/jobsubmitter.py | 3 ++- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 900c1f23..bb6fa262 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -171,9 +171,9 @@ def command_processor(self, command_dict): def reset_job_to_created(job_id): job = Job.objects.get(id=job_id) - clean_directory(job.job_store_location) - clean_directory(job.working_dir) - clean_directory(job.log_dir) + clean_directory(job, job.job_store_location) + clean_directory(job, job.working_dir) + clean_directory(job, job.log_dir) job.job_store_location = "" job.working_dir = "" job.status = Status.CREATED diff --git a/submitter/factory.py b/submitter/factory.py index e01c95d8..1209f591 100644 --- a/submitter/factory.py +++ b/submitter/factory.py @@ -33,7 +33,7 @@ def factory( log_dir, log_prefix, app_name, - user, + user=user, ) elif type == PipelineType.NEXTFLOW: return NextflowJobSubmitter( @@ -48,5 +48,5 @@ def factory( log_dir, log_prefix, app_name, - user, + user=user, ) diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index bf7c42af..a122774c 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -70,9 +70,10 @@ def _prepare_directories(self): if not os.path.exists(self.job_work_dir): os.mkdir(self.job_work_dir) - if self.user or self.group: + if self.user: shutil.chown( self.job_work_dir, + user=self.user ) if os.path.exists(self.job_store_dir) and not self.resume_jobstore: diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 87e26225..d44df3f5 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -61,8 +61,8 @@ def __init__( log_dir, log_prefix, app_name, - root_permissions, - user, + root_permissions=root_permissions, + user=user, ) self.resume_jobstore = resume_jobstore dir_config = settings.PIPELINE_CONFIG.get(self.app_name) From fbefbed6cb8bda5481cb5637f3f95a626f36e8a7 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:37:15 -0400 Subject: [PATCH 136/235] Better handling of edge cases --- orchestrator/tasks.py | 9 ++++++++- submitter/toil_submitter/toil_track_utils.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index bb6fa262..33d238c4 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -167,6 +167,9 @@ def command_processor(self, command_dict): logger.error(f"Command {str(command.command_type)} failed. Not retrying. Exception {str(e)}") if command.command_type == CommandType.SUBMIT: reset_job_to_created(command.job_id) + elif command.command_type == CommandType.PREPARE: + job = Job.objects.get(id=command.job_id) + _fail(job,str(e)) def reset_job_to_created(job_id): @@ -211,7 +214,9 @@ def prepare_job(job): try: job_store_dir, job_work_dir, job_output_dir, log_dir, log_prefix = submitter.prepare_to_submit() except Exception as e: - raise RetryException(f"Failed to fetch status for job {str(job.id)} {e}") + if job.resume_job_store_location and not os.path.exists(job.resume_job_store_location): + raise StopException(f"Stopping job preparation, {e}") + raise RetryException(f"Failed to prepare the job {str(job.id)} {e}") else: job.job_prepared(job_store_dir, job_work_dir, job_output_dir, log_dir, log_prefix) @@ -577,6 +582,8 @@ def cleanup_folders(self, job_id, exclude, job_store=True, work_dir=True): @userswitch def clean_directory(job, path, exclude=[]): + if not path: + return False with tempfile.TemporaryDirectory() as tmpdirname: for f in exclude: src = os.path.join(path, f) diff --git a/submitter/toil_submitter/toil_track_utils.py b/submitter/toil_submitter/toil_track_utils.py index 25635425..4c4a16a6 100755 --- a/submitter/toil_submitter/toil_track_utils.py +++ b/submitter/toil_submitter/toil_track_utils.py @@ -804,6 +804,8 @@ def check_status(self): except JobException: logger.warning("No job has been set as the root in this job store") return + except Exception: + raise StopException("This job was run by a version of TOIL that is not supported") if not root_job: logger.warning("RootJob couldn't be fetched") raise StopException("RootJob couldn't be fetched") From 2180681ca772c553af8dc0127352f816d4810caa Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:37:41 -0400 Subject: [PATCH 137/235] Update user script for user specific functions --- submitter/userswitcher.py | 62 +++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index b5445e4d..33b2af54 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -1,3 +1,4 @@ +import os import sys import marshal import subprocess @@ -8,25 +9,41 @@ from pathlib import Path from functools import wraps from getpass import getuser +import django +import tempfile +import json from django.conf import settings log = logging.getLogger(__name__) def userscript(): + + ridgeback_path = os.environ.get("RIDGEBACK_PATH","/usr/bin/ridgeback") + sys.path.append(ridgeback_path) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ridgeback.settings") stdout_buffer = io.StringIO() stderr_buffer = io.StringIO() exception_raised = False + output = None with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): try: - func_serialized, args, kwargs = marshal.loads(sys.stdin.buffer.read()) - func = dill.loads(func_serialized) + env_str = sys.argv[1] + env_json = json.loads(env_str) + for single_env in env_json: + if single_env=='PATH': + os.environ[single_env] = env_json[single_env] + else: + os.environ.setdefault(single_env,env_json[single_env]) + django.setup() + func_data = sys.stdin.buffer.read() + func, args, kwargs = dill.loads(func_data) output = func(*args, **kwargs) except Exception: log.exception("Exception when running the function as another user") exception_raised = True - script_tuple = (output, stdout_buffer.getvalue()) - sys.stderr.buffer.write(stderr_buffer.getvalue()) + script_tuple = (output, stdout_buffer.getvalue().encode()) + sys.stderr.buffer.write(stderr_buffer.getvalue().encode()) sys.stdout.buffer.write(dill.dumps(script_tuple)) if exception_raised: sys.exit(1) @@ -37,25 +54,44 @@ def userswitch(func): def dzdo_wrapper(*args, **kwargs): # jobsubmitter/batchsystem objects will have the user attribute in self user = args[0].user + current_env = {} if user == getuser() or not settings.ENABLE_USER_SWITCH: return func(*args, **kwargs) else: - proc_command = ["dzdo", "-u", f"{user}", sys.executable, "-c", Path(__file__).absolute()] - serialized_func = dill.dumps(func) - func_data = marshal.dumps((serialized_func, args, kwargs)) + for key, value in os.environ.items(): + current_env[key] = value + proc_command = ["dzdo", "--login", "-u", f"{user}", sys.executable, Path(__file__).absolute()] try: - dzdo_process = subprocess.run(proc_command, input=func_data, check=True, capture_output=True) - log.error(dzdo_process.stderr) + job_func = dill.dumps((func, args, kwargs)) + env_str = json.dumps(current_env) + dzdo_process = subprocess.run(proc_command+[env_str], input=job_func, check=True, capture_output=True, env=current_env) output, stdout = dill.loads(dzdo_process.stdout) - log.info(stdout) + func_stdout = stdout.decode().strip() + func_stderr = dzdo_process.stderr.decode().strip() + if func_stdout: + log.info(func_stdout) + if func_stderr: + log.info(func_stderr) + log.error(func_stderr) return output except subprocess.CalledProcessError as e: + stdout_str = "" + stderr_str = "" + try: + output, stdout = dill.loads(e.output) + if stdout: + stdout_str = stdout.decode().strip() + stderr = e.stderr + if stderr: + stderr_str = stderr.decode().strip() + except: + stdout_str = "NA" exception_message = f""" Error while userswitching: - Command: {e.cmd} + USER: {user} Return Code: {e.returncode} - Output: {e.output} - Error: {e.stderr} + Output: {stdout_str} + Error: {stderr_str} """ raise Exception(exception_message) except FileNotFoundError as e: From 999ace30130aab851854132de0f405cd5119d7e7 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:47:07 -0400 Subject: [PATCH 138/235] Add black formatting --- orchestrator/tasks.py | 2 +- ridgeback/settings.py | 2 +- submitter/jobsubmitter.py | 5 +---- submitter/toil_submitter/toil_jobsubmitter.py | 2 +- submitter/userswitcher.py | 12 +++++++----- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 33d238c4..efc414f9 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -169,7 +169,7 @@ def command_processor(self, command_dict): reset_job_to_created(command.job_id) elif command.command_type == CommandType.PREPARE: job = Job.objects.get(id=command.job_id) - _fail(job,str(e)) + _fail(job, str(e)) def reset_job_to_created(job_id): diff --git a/ridgeback/settings.py b/ridgeback/settings.py index fd8fe03c..757d8f9b 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -322,4 +322,4 @@ "ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin" ) -# SHELL_PLUS = "ipython" \ No newline at end of file +# SHELL_PLUS = "ipython" diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index a122774c..7bd319e4 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -71,10 +71,7 @@ def _prepare_directories(self): if not os.path.exists(self.job_work_dir): os.mkdir(self.job_work_dir) if self.user: - shutil.chown( - self.job_work_dir, - user=self.user - ) + shutil.chown(self.job_work_dir, user=self.user) if os.path.exists(self.job_store_dir) and not self.resume_jobstore: diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 45345a46..ea7d93a8 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -84,7 +84,7 @@ def prepare_to_submit(self): @userswitch def resolve_app(self): self.app.resolve(self.job_work_dir) - + def get_submit_command(self): command_line = self._command_line() log_path = os.path.join(self.job_work_dir, self.batch_system.logfileName) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 33b2af54..7e532b00 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -18,8 +18,8 @@ def userscript(): - - ridgeback_path = os.environ.get("RIDGEBACK_PATH","/usr/bin/ridgeback") + + ridgeback_path = os.environ.get("RIDGEBACK_PATH", "/usr/bin/ridgeback") sys.path.append(ridgeback_path) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ridgeback.settings") stdout_buffer = io.StringIO() @@ -31,10 +31,10 @@ def userscript(): env_str = sys.argv[1] env_json = json.loads(env_str) for single_env in env_json: - if single_env=='PATH': + if single_env == "PATH": os.environ[single_env] = env_json[single_env] else: - os.environ.setdefault(single_env,env_json[single_env]) + os.environ.setdefault(single_env, env_json[single_env]) django.setup() func_data = sys.stdin.buffer.read() func, args, kwargs = dill.loads(func_data) @@ -64,7 +64,9 @@ def dzdo_wrapper(*args, **kwargs): try: job_func = dill.dumps((func, args, kwargs)) env_str = json.dumps(current_env) - dzdo_process = subprocess.run(proc_command+[env_str], input=job_func, check=True, capture_output=True, env=current_env) + dzdo_process = subprocess.run( + proc_command + [env_str], input=job_func, check=True, capture_output=True, env=current_env + ) output, stdout = dill.loads(dzdo_process.stdout) func_stdout = stdout.decode().strip() func_stderr = dzdo_process.stderr.decode().strip() From 1760d355ba86191a62954730efa5638de98dae9d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 12:58:28 -0400 Subject: [PATCH 139/235] Added groups for compose --- compose.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compose.yaml b/compose.yaml index 0b687598..db27d551 100644 --- a/compose.yaml +++ b/compose.yaml @@ -31,7 +31,8 @@ x-ridgeback_celery: &ridgeback_celery user: root env_file: .env group_add: - - ${DOCKER_GROUPS} + - ${DOCKER_GROUP_1} + - ${DOCKER_GROUP_2} environment: - RIDGEBACK_DB_URL=ridgeback_postgres - RIDGEBACK_MEMCACHED_HOST=ridgeback_memcached @@ -139,6 +140,7 @@ services: - | chown -R ${DOCKER_UID}:${DOCKER_GID} /postgres chown -R ${DOCKER_UID}:${DOCKER_GID} /logs + chmod -R 777 /logs chown -R ${DOCKER_UID}:${DOCKER_GID} /celery chown -R ${DOCKER_UID}:${DOCKER_GID} /rabbitmq chown -R ${DOCKER_UID}:${DOCKER_GID} /server @@ -250,7 +252,8 @@ services: - voyager_net env_file: .env group_add: - - ${DOCKER_GROUPS} + - ${DOCKER_GROUP_1} + - ${DOCKER_GROUP_2} environment: - RIDGEBACK_DB_URL=ridgeback_postgres - RIDGEBACK_MEMCACHED_HOST=ridgeback_memcached From 6979b37bd1f6c4d04d732f50bf8f73e3b12714a1 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 13:12:07 -0400 Subject: [PATCH 140/235] Flake8 fixes --- submitter/userswitcher.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 7e532b00..d6f6a47a 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -1,6 +1,5 @@ import os import sys -import marshal import subprocess import dill import contextlib @@ -10,7 +9,6 @@ from functools import wraps from getpass import getuser import django -import tempfile import json from django.conf import settings @@ -86,7 +84,7 @@ def dzdo_wrapper(*args, **kwargs): stderr = e.stderr if stderr: stderr_str = stderr.decode().strip() - except: + except Exception: stdout_str = "NA" exception_message = f""" Error while userswitching: From 7d883d888154ef0e7185ad63725ef77e71d8e91a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 13 Aug 2025 13:23:38 -0400 Subject: [PATCH 141/235] Lock postgres to os version --- compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index db27d551..83d572e9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -147,7 +147,7 @@ services: chown -R ${DOCKER_UID}:${DOCKER_GID} /logrotate chown -R ${DOCKER_UID}:${DOCKER_GID} /db_backup ridgeback_postgres: - image: postgres:17 + image: postgres:17-trixie restart: on-failure user: "${DOCKER_UID}:${DOCKER_GID}" networks: From e83b497937828318183ebc17f6f7ee4f13b7aade Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 19 Aug 2025 16:04:39 -0400 Subject: [PATCH 142/235] Changes to allow multiple instances to run concurrently --- compose.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compose.yaml b/compose.yaml index 83d572e9..2071d264 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,4 +1,4 @@ -name: "Ridgeback Services" +name: "Ridgeback Services ${RIDGEBACK_DEPLOYMENT}" x-ridgeback_celery: &ridgeback_celery image: mskcc/ridgeback:${RIDGEBACK_VERSION} @@ -51,6 +51,7 @@ x-ridgeback_celery: &ridgeback_celery - ${CLUSTER_FILESYSTEM_SHARE_MOUNT}:${CLUSTER_FILESYSTEM_SHARE_MOUNT} - ${CLUSTER_FILESYSTEM_COLLAB}:${CLUSTER_FILESYSTEM_COLLAB} - ${CLUSTER_FILESYSTEM_TEST}:${CLUSTER_FILESYSTEM_TEST} + - ${CLUSTER_CODE_PATH:-/dev/null}:${CLUSTER_CODE_PATH:-/dev/null} - type: bind source: ${SLURM_ETC} target: ${SLURM_ETC} @@ -265,6 +266,8 @@ services: volumes: - ./logs/:/ridgeback/server/ - ./server/:/ridgeback_staticfiles/ + - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} + - ${CLUSTER_CODE_PATH:-/dev/null}:${CLUSTER_CODE_PATH:-/dev/null} ports: - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} post_start: @@ -516,5 +519,5 @@ services: restart: false networks: voyager_net: - name: voyager_network + name: voyager_network_${RIDGEBACK_DEPLOYMENT} driver: bridge From c2703dc2e97c83947aedf9f16df1d7702fd9a61e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:02:14 -0400 Subject: [PATCH 143/235] Updated build for Docker --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a7abf820..f5f9d151 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-slim +FROM python:3.11-slim LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ version.image="1.0.0" \ @@ -32,6 +32,8 @@ RUN apt-get update \ # Clean up image && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ + # For venv setup + && apt-get -y python3.11-dev libpq-dev \ && rm -rf /var/lib/apt/lists/* From 999eb16ea4c2d54613dd601474472ec654e3d10e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:06:00 -0400 Subject: [PATCH 144/235] Corrected python dev install step --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f5f9d151..c63744a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ # For venv setup - && apt-get -y python3.11-dev libpq-dev \ + && apt-get -y python3-dev libpq-dev \ && rm -rf /var/lib/apt/lists/* From 7d8e54f8fa211685ffc19fbbbc02426120af4290 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:09:02 -0400 Subject: [PATCH 145/235] Fixed typo --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c63744a8..ccd6277c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ # For venv setup - && apt-get -y python3-dev libpq-dev \ + && apt-get install -y python3.11-dev libpq-dev \ && rm -rf /var/lib/apt/lists/* From 4c34c3ec697ef46b3bbc6f33177c289244c30b8a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:12:41 -0400 Subject: [PATCH 146/235] Fixing python install --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ccd6277c..e244bca9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ # For venv setup - && apt-get install -y python3.11-dev libpq-dev \ + && apt-get install -y python3-dev libpq-dev \ && rm -rf /var/lib/apt/lists/* From 2fe7a873c1cdcc442b7f4baa4de6cd9c02e37bc7 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:16:54 -0400 Subject: [PATCH 147/235] Install the proper python version --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e244bca9..c381d1d9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,9 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ # For venv setup - && apt-get install -y python3-dev libpq-dev \ + && add-apt-repository ppa:deadsnakes/ppa -y \ + && apt update \ + && apt-get install -y python3.11-dev libpq-dev \ && rm -rf /var/lib/apt/lists/* From f23b28bb8d7e46f4b1d5ef720798fe87a4c081e2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:20:12 -0400 Subject: [PATCH 148/235] Update python install --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index c381d1d9..9db780b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,7 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ # For venv setup + && apt-get -y software-properties-common \ && add-apt-repository ppa:deadsnakes/ppa -y \ && apt update \ && apt-get install -y python3.11-dev libpq-dev \ From 42e7afba31de28ec3781fa340342ca81351c9410 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:22:39 -0400 Subject: [PATCH 149/235] Update apt install --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9db780b7..52d83b6b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y --no-install-recommends install openssh-client \ # For venv setup - && apt-get -y software-properties-common \ + && apt-get -y install software-properties-common \ && add-apt-repository ppa:deadsnakes/ppa -y \ && apt update \ && apt-get install -y python3.11-dev libpq-dev \ From ebb9d629e12bcf56ec66bbe990f2123f5b45c88a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 17:40:01 -0400 Subject: [PATCH 150/235] Some more image fixes --- Dockerfile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 52d83b6b..afcada07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,12 +31,9 @@ RUN apt-get update \ && pip3 install --use-pep517 -r requirements-toil.txt \ # Clean up image && apt-get -y purge --auto-remove build-essential \ - && apt-get -y --no-install-recommends install openssh-client \ + && apt-get -y purge --auto-remove openssh-client \ # For venv setup - && apt-get -y install software-properties-common \ - && add-apt-repository ppa:deadsnakes/ppa -y \ - && apt update \ - && apt-get install -y python3.11-dev libpq-dev \ + && apt-get install -y libpq-dev \ && rm -rf /var/lib/apt/lists/* From 10b3137f82d43e26f2e87681869311a24b7b1989 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 18:04:15 -0400 Subject: [PATCH 151/235] Fixed docker image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index afcada07..c0f0b329 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN apt-get update \ && apt-get -y purge --auto-remove build-essential \ && apt-get -y purge --auto-remove openssh-client \ # For venv setup - && apt-get install -y libpq-dev \ + && apt-get install -y ca-certificates libpq-dev \ && rm -rf /var/lib/apt/lists/* From 58400798297f41a0f3669aafa27783294e997fff Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 18:18:55 -0400 Subject: [PATCH 152/235] Update image for python 3.10 --- Dockerfile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index c0f0b329..18d97fac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11-slim +FROM python:3.10-slim LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ version.image="1.0.0" \ @@ -13,7 +13,7 @@ RUN apt-get update \ # Install dependencies && apt-get -y --no-install-recommends install \ wget curl libldap2-dev libsasl2-dev procps libssl-dev libxml2-dev libxslt-dev \ - libpq-dev gawk nodejs git build-essential \ + libpq-dev gawk nodejs git build-essential openssh-client \ # Install Ridgeback && cd /usr/bin \ && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ @@ -31,9 +31,6 @@ RUN apt-get update \ && pip3 install --use-pep517 -r requirements-toil.txt \ # Clean up image && apt-get -y purge --auto-remove build-essential \ - && apt-get -y purge --auto-remove openssh-client \ - # For venv setup - && apt-get install -y ca-certificates libpq-dev \ && rm -rf /var/lib/apt/lists/* From 97dac2b70cb7af7b12f731fc778cfcc997337a8f Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 2 Sep 2025 18:51:10 -0400 Subject: [PATCH 153/235] Keep build essential for venv building --- Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 18d97fac..818a98e1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,6 @@ RUN apt-get update \ && pip3 install --use-pep517 -r requirements.txt \ && pip3 install --use-pep517 -r requirements-toil.txt \ # Clean up image - && apt-get -y purge --auto-remove build-essential \ && rm -rf /var/lib/apt/lists/* From 2c15d90dd8fce8fc5b1d41ce435210ea650ff0f0 Mon Sep 17 00:00:00 2001 From: buehlere Date: Thu, 4 Sep 2025 15:47:55 -0400 Subject: [PATCH 154/235] more general toil output parse --- submitter/toil_submitter/toil_jobsubmitter.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index ea7d93a8..cd04c5cd 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -161,14 +161,15 @@ def get_outputs(self): data = f.readlines() data = "".join(data) substring = data.split("\n{")[1] + # Keep original opening brace + result = "{" + substring if "-----------" in substring: - result = ("{" + substring).split("-----------")[0] - elif "\n}\n" in substring: - result_segment = substring.split("\n}\n")[0] - result = "{" + result_segment + "}" + # Handle the special marker case + result = result[: result.rfind("-----------")] else: - result_segment = substring.split("}[")[0] - result = "{" + result_segment + "}" + last_brace_idx = result.rfind("}") + result = result[: last_brace_idx + 1] + # Now it's safe to parse result_json = json.loads(result) except (IndexError, ValueError): error_message = "Could not parse json from %s" % log_path From 508a1acc8c98632de264f98631245f0d528b9711 Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 9 Sep 2025 12:58:53 -0400 Subject: [PATCH 155/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 1 - 1 file changed, 1 deletion(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index cd04c5cd..349e53a6 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -273,7 +273,6 @@ def _command_line(self): "20G", "--not-strict", "--runCwlInternalJobsOnWorkers", - "--realTimeLogging", "--jobStore", self.job_store_dir, "--tmpdir-prefix", From 8d400ef646d0afcd4bebd1276c62399ce54418b1 Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 9 Sep 2025 14:01:31 -0400 Subject: [PATCH 156/235] Update slurm_client.py --- batch_systems/slurm_client/slurm_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index dbe6c7af..5618fbc8 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -69,6 +69,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): sbatch_command, check=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, universal_newlines=True, cwd=work_dir, env=current_env, From 488f5b2de746090a9263fc3cd5f42763c6249ade Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 9 Sep 2025 14:32:49 -0400 Subject: [PATCH 157/235] Update slurm_client.py --- batch_systems/slurm_client/slurm_client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 5618fbc8..fe197067 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -28,6 +28,7 @@ def __init__(self, user=getuser()): """ self.logger = logging.getLogger("SLURM_client") self.logfileName = "slurm.log" + self.errorlogfileName = "slurm_error.log" self.name = "slurm" self.user = user @@ -145,8 +146,8 @@ def set_group(self, group_id): def set_stdout_file(self, stdout_file): if stdout_file: - return [f"--output={stdout_file}"] - return [f"--output={self.logfileName}"] + return [f"--output={stdout_file}", f"--error={self.errorlogfileName}"] + return [f"--output={self.logfileName}", f"--error={self.errorlogfileName}"] def set_service_queue(self): service_queue_args = [] From 7e346c29262a0a89d9127745be1740c8de42a470 Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 9 Sep 2025 15:45:18 -0400 Subject: [PATCH 158/235] Revert "Update slurm_client.py" This reverts commit 8d400ef646d0afcd4bebd1276c62399ce54418b1. --- batch_systems/slurm_client/slurm_client.py | 1 - 1 file changed, 1 deletion(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index fe197067..e6e5d128 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -70,7 +70,6 @@ def submit(self, command, job_args, stdout, job_id, env={}): sbatch_command, check=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, universal_newlines=True, cwd=work_dir, env=current_env, From 8e08de6e4e614f5860befe98e3f6b643488d34be Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 9 Sep 2025 15:45:24 -0400 Subject: [PATCH 159/235] Revert "Update slurm_client.py" This reverts commit 488f5b2de746090a9263fc3cd5f42763c6249ade. --- batch_systems/slurm_client/slurm_client.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index e6e5d128..dbe6c7af 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -28,7 +28,6 @@ def __init__(self, user=getuser()): """ self.logger = logging.getLogger("SLURM_client") self.logfileName = "slurm.log" - self.errorlogfileName = "slurm_error.log" self.name = "slurm" self.user = user @@ -145,8 +144,8 @@ def set_group(self, group_id): def set_stdout_file(self, stdout_file): if stdout_file: - return [f"--output={stdout_file}", f"--error={self.errorlogfileName}"] - return [f"--output={self.logfileName}", f"--error={self.errorlogfileName}"] + return [f"--output={stdout_file}"] + return [f"--output={self.logfileName}"] def set_service_queue(self): service_queue_args = [] From 8ce5f8328236d237f4335e6e00ebf342fec9db77 Mon Sep 17 00:00:00 2001 From: buehlere Date: Tue, 9 Sep 2025 16:19:25 -0400 Subject: [PATCH 160/235] Update toil_jobsubmitter.py --- submitter/toil_submitter/toil_jobsubmitter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 349e53a6..0bdc0cfb 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -242,6 +242,7 @@ def _command_line(self): """ command_line = [ "toil-cwl-runner", + "--logLevel=INFO", "--no-container", "--logFile", "toil_log.log", From fbd62502e4e6aa8c10e70539fee453e3d568c845 Mon Sep 17 00:00:00 2001 From: buehlere Date: Wed, 10 Sep 2025 12:15:35 -0400 Subject: [PATCH 161/235] remove leaking jobstore message --- submitter/toil_submitter/toil_jobsubmitter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 0bdc0cfb..b978dfe7 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -1,4 +1,5 @@ import os +import re import json import copy from django.conf import settings @@ -161,6 +162,7 @@ def get_outputs(self): data = f.readlines() data = "".join(data) substring = data.split("\n{")[1] + substring = re.sub(r"is.*mskcc\.org.*Successfully deleted the job store.*\)\r?\n", "", substring) # Keep original opening brace result = "{" + substring if "-----------" in substring: From 73edd9bf5c0ab8b02116f2efbcc5211524406a10 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 10:22:13 -0400 Subject: [PATCH 162/235] Fixed python installation --- compose.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compose.yaml b/compose.yaml index 2071d264..3fd01289 100644 --- a/compose.yaml +++ b/compose.yaml @@ -51,6 +51,10 @@ x-ridgeback_celery: &ridgeback_celery - ${CLUSTER_FILESYSTEM_SHARE_MOUNT}:${CLUSTER_FILESYSTEM_SHARE_MOUNT} - ${CLUSTER_FILESYSTEM_COLLAB}:${CLUSTER_FILESYSTEM_COLLAB} - ${CLUSTER_FILESYSTEM_TEST}:${CLUSTER_FILESYSTEM_TEST} + - ${CLUSTER_PYTHON_MOUNT}:${CLUSTER_PYTHON_MOUNT} + - ${CLUSTER_PYTHON_LIBSO_MOUNT}:${CONTAINER_PYTHON_LIBSO_MOUNT} + - ${CLUSTER_PYTHON_LIB_MOUNT}:${CLUSTER_PYTHON_LIB_MOUNT} + - ${CLUSTER_PYTHON_INCLUDE_MOUNT}:${CLUSTER_PYTHON_INCLUDE_MOUNT} - ${CLUSTER_CODE_PATH:-/dev/null}:${CLUSTER_CODE_PATH:-/dev/null} - type: bind source: ${SLURM_ETC} @@ -312,14 +316,16 @@ services: <<: *ridgeback_celery command: - | + source ${RIDGEBACK_VENV}/bin/activate + if ! python3 -c "import toil" 2>&1 >/dev/null then pip3 install --upgrade pip && pip3 install --force-reinstall 'setuptools<58.0.0' && pip3 install "cython<3.0.0" wheel && pip3 install "pyyaml==5.4.1" --no-build-isolation && - pip3 install -r ${RIDGEBACK_PATH}/requirements.txt && - pip3 install -r ${RIDGEBACK_PATH}/requirements-toil.txt + pip3 install --use-pep517 -r ${RIDGEBACK_PATH}/requirements.txt && + pip3 install --use-pep517 -r ${RIDGEBACK_PATH}/requirements-toil.txt fi PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid @@ -340,7 +346,7 @@ services: "ps -p $(pgrep -F /ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid)", ] interval: 30s - timeout: 3s + timeout: 10s retries: 3 depends_on: ridgeback_postgres: From cc9d98b081c5296baed17f1fae4a0e965532be14 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 10:30:19 -0400 Subject: [PATCH 163/235] Fixed compose healthchecks --- compose.yaml | 56 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/compose.yaml b/compose.yaml index 3fd01289..645645da 100644 --- a/compose.yaml +++ b/compose.yaml @@ -376,6 +376,11 @@ services: --pidfile $$PIDFILE \ --concurrency=30 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} || exit 1" + interval: 30s + timeout: 3s + retries: 3 ridgeback_celery_action_queue: <<: *ridgeback_celery command: @@ -393,6 +398,11 @@ services: --pidfile $$PIDFILE \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} || exit 1" + interval: 30s + timeout: 3s + retries: 3 ridgeback_celery_check_status_queue: <<: *ridgeback_celery command: @@ -409,7 +419,13 @@ services: -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ --pidfile $$PIDFILE \ - --concurrency=10 + --concurrency=10 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} || exit 1" + interval: 30s + timeout: 3s + retries: 3 ridgeback_celery_submit_job_queue: <<: *ridgeback_celery command: @@ -426,7 +442,13 @@ services: -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ --pidfile $$PIDFILE \ - --concurrency=5 + --concurrency=5 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} || exit 1" + interval: 30s + timeout: 3s + retries: 3 ridgeback_celery_set_permission_queue: <<: *ridgeback_celery command: @@ -443,7 +465,13 @@ services: -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ --pidfile $$PIDFILE \ - --concurrency=10 + --concurrency=10 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} || exit 1" + interval: 30s + timeout: 3s + retries: 3 ridgeback_celery_cleanup_queue: <<: *ridgeback_celery command: @@ -460,7 +488,13 @@ services: -Q ${RIDGEBACK_CLEANUP_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ --pidfile $$PIDFILE \ - --concurrency=2 + --concurrency=2 \ + -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} + healthcheck: + test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} || exit 1" + interval: 30s + timeout: 3s + retries: 3 ridgeback_logrotate: image: mskcc/voyager-compose-utils:1.0.0 restart: always @@ -487,9 +521,14 @@ services: } EOF cat > /logrotate/logrotate.cron <> /logs/logrotate_cron.log 2>&1 + healthcheck: + test: "find /logs/last_completed_logrotate_cron -type f -mtime -2 | read" + interval: 12h + timeout: 5h + retries: 3 depends_on: ridgeback_celery_beat: condition: service_healthy @@ -513,9 +552,14 @@ services: - | mkdir -p /db_backup/archive cat > /db_backup/db_backup.cron <> /logs/db_backup_cron.log 2>&1 + healthcheck: + test: "find /logs/last_completed_db_backup_cron -type f -mtime -2 | read" + interval: 12h + timeout: 5h + retries: 3 depends_on: ridgeback_celery_beat: condition: service_healthy From c0586d23b989a9bea95e0d3829abe4133fd003f9 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 10:49:01 -0400 Subject: [PATCH 164/235] Formatting fix --- compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index 645645da..2dd26fa6 100644 --- a/compose.yaml +++ b/compose.yaml @@ -570,4 +570,4 @@ services: networks: voyager_net: name: voyager_network_${RIDGEBACK_DEPLOYMENT} - driver: bridge + driver: bridge \ No newline at end of file From e58f91dc50d2f7f647459d6d3daa7fdef667883e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 16:48:41 -0400 Subject: [PATCH 165/235] Fix flake --- ridgeback/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/__init__.py b/ridgeback/__init__.py index f4d12086..773c90f9 100644 --- a/ridgeback/__init__.py +++ b/ridgeback/__init__.py @@ -1 +1 @@ -__version__ = "1.39.0" \ No newline at end of file +__version__ = "1.39.0" From 7baf9c5d5f74997580ecc0377bbfb33c67ed524c Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 17:00:44 -0400 Subject: [PATCH 166/235] Update compose utils --- compose.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compose.yaml b/compose.yaml index 2dd26fa6..88543614 100644 --- a/compose.yaml +++ b/compose.yaml @@ -496,7 +496,7 @@ services: timeout: 3s retries: 3 ridgeback_logrotate: - image: mskcc/voyager-compose-utils:1.0.0 + image: mskcc/voyager-compose-utils:1.0.1 restart: always user: "${DOCKER_UID}:${DOCKER_GID}" networks: @@ -537,7 +537,7 @@ services: condition: service_healthy restart: false ridgeback_db_backup: - image: mskcc/voyager-compose-utils:1.0.0 + image: mskcc/voyager-compose-utils:1.0.1 restart: always user: "${DOCKER_UID}:${DOCKER_GID}" networks: @@ -570,4 +570,4 @@ services: networks: voyager_net: name: voyager_network_${RIDGEBACK_DEPLOYMENT} - driver: bridge \ No newline at end of file + driver: bridge From e859181f5c932e911db092fb1567f9987ec52654 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 17:10:49 -0400 Subject: [PATCH 167/235] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c404fb3a..987f68f6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Ridgeback -API for running Toil jobs. +API for running Toil and Nextflow jobs. Supports LSF, SLURM, and singleMachine mode ## Filebeat From 5ff20d427afff5ffd404fdf82293c33fe09433f2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 17:14:28 -0400 Subject: [PATCH 168/235] Added labels to Dockerfile --- Dockerfile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 818a98e1..c2cbba04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,13 @@ FROM python:3.10-slim -LABEL maintainer="Nikhil Kumar (kumarn1@mskcc.org)" \ - version.image="1.0.0" \ - source.ridgeback="https://github.com/mskcc/ridgeback" +LABEL org.opencontainers.image.vendor="MSKCC" \ + org.opencontainers.image.authors="Nikhil Kumar (kumarn1@mskcc.org)" \ + org.opencontainers.image.created="2025-09-15T16:04:00Z" \ + org.opencontainers.image.licenses="Apache-2.0" \ + org.opencontainers.image.version="1.0.0" \ + org.opencontainers.image.source="https://github.com/mskcc/ridgeback" \ + org.opencontainers.image.title="Ridgeback" \ + org.opencontainers.image.description="API for running Toil and Nextflow jobs, supports LSF, SLURM, and singleMachine mode" ENV DEBIAN_FRONTEND noninteractive ENV PIP_ROOT_USER_ACTION ignore @@ -15,9 +20,7 @@ RUN apt-get update \ wget curl libldap2-dev libsasl2-dev procps libssl-dev libxml2-dev libxslt-dev \ libpq-dev gawk nodejs git build-essential openssh-client \ # Install Ridgeback - && cd /usr/bin \ - && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH \ - && cd /usr/bin/ridgeback \ + && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH /usr/bin/ridgeback\ # Install alternative ssl library && wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ && dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ From 557ffc5b2758a2556178da90326c78bf8258aac1 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 15 Sep 2025 17:18:45 -0400 Subject: [PATCH 169/235] Update requirements path in Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c2cbba04..2d92a0ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,8 +30,8 @@ RUN apt-get update \ # Install python packages && pip3 install --upgrade pip \ && pip3 install python-ldap \ - && pip3 install --use-pep517 -r requirements.txt \ - && pip3 install --use-pep517 -r requirements-toil.txt \ + && pip3 install --use-pep517 -r /usr/bin/ridgeback/requirements.txt \ + && pip3 install --use-pep517 -r /usr/bin/ridgeback/requirements-toil.txt \ # Clean up image && rm -rf /var/lib/apt/lists/* From 4d3131b17df8d0f36a60c43595043c925f4511f2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 17 Sep 2025 14:00:40 -0400 Subject: [PATCH 170/235] Print error logs only when they exist --- submitter/userswitcher.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index d6f6a47a..8f671a33 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -71,8 +71,7 @@ def dzdo_wrapper(*args, **kwargs): if func_stdout: log.info(func_stdout) if func_stderr: - log.info(func_stderr) - log.error(func_stderr) + log.error(func_stderr) return output except subprocess.CalledProcessError as e: stdout_str = "" From 766da3ef00fbea04851cabf98fc7d329745b7964 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 17 Sep 2025 14:00:57 -0400 Subject: [PATCH 171/235] Preserve errors on exception --- submitter/userswitcher.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 8f671a33..53e3e6b0 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -77,13 +77,13 @@ def dzdo_wrapper(*args, **kwargs): stdout_str = "" stderr_str = "" try: - output, stdout = dill.loads(e.output) - if stdout: - stdout_str = stdout.decode().strip() stderr = e.stderr if stderr: stderr_str = stderr.decode().strip() - except Exception: + output, stdout = dill.loads(e.output) + if stdout: + stdout_str = stdout.decode().strip() + except Exception as error: stdout_str = "NA" exception_message = f""" Error while userswitching: From 85a4811361405eda93eb9440ff9ad8e4c30294db Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 17 Sep 2025 14:14:19 -0400 Subject: [PATCH 172/235] Added mircobiome pipeline path --- ridgeback/settings.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 757d8f9b..70f8e53d 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -263,6 +263,11 @@ "WORK_DIR_ROOT": os.environ["ACCESS_HEME_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["ACCESS_HEME_TMP_DIR_ROOT"], }, + "MICROBIOME": { + "JOB_STORE_ROOT": os.environ["MICROBIOME_JOB_STORE_ROOT"], + "WORK_DIR_ROOT": os.environ["MICROBIOME_WORK_DIR_ROOT"], + "TMP_DIR_ROOT": os.environ["MICROBIOME_TMP_DIR_ROOT"], + }, "NA": { "JOB_STORE_ROOT": os.environ["DEFAULT_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["DEFAULT_WORK_DIR_ROOT"], From a878943173a62afd63ffb374f28a0cf13d901f4d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 18 Sep 2025 14:07:27 -0400 Subject: [PATCH 173/235] Added extra user switch commands --- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 2 ++ submitter/toil_submitter/toil_jobsubmitter.py | 1 + 2 files changed, 3 insertions(+) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 59a6d7b5..07c68415 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -86,6 +86,7 @@ def prepare_to_submit(self): self._dump_app_inputs() return self.job_store_dir, self.job_work_dir, self.job_outputs_dir, self.log_dir, self.log_prefix + @userswitch def get_submit_command(self): command_line = self._command_line() log_path = os.path.join(self.job_work_dir, self.batch_system.logfileName) @@ -159,6 +160,7 @@ def _output_construct(self, path): } return file_obj + @userswitch def get_outputs(self): error_message = None result = list() diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index b978dfe7..6511de5b 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -153,6 +153,7 @@ def get_commandline_status(self, cache): return job_safe, track_cache_safe + @userswitch def get_outputs(self): error_message = None result_json = None From ca2e04b3d755aeb043034864d2f058d2f068a3a1 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 18 Sep 2025 14:07:58 -0400 Subject: [PATCH 174/235] Simplify get job info path --- orchestrator/tasks.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 5c3b55af..7f2a99f5 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -27,9 +27,7 @@ def get_job_info_path(job_id): job = Job.objects.get(id=job_id) - work_dir = os.path.join( - settings.PIPELINE_CONFIG.get(job.metadata["pipeline_name"], "NA")["WORK_DIR_ROOT"], str(job_id) - ) + work_dir = job.working_dir job_info_path = os.path.join(work_dir, ".run.info") return job_info_path From f86bdb2ad5d27ebeddb70804867ef4fb4ce78c68 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 19 Sep 2025 16:40:09 -0400 Subject: [PATCH 175/235] Added condition to handle empty profile --- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 07c68415..41f7d385 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -237,6 +237,8 @@ def _dump_config(self, config): def _command_line(self): profile = self.inputs["profile"] + if not profile: + profile = "''" params = self.inputs.get("params", {}) command_line = [ settings.NEXTFLOW, From 052e62ecf531855995034633712ec4fdd1a309c8 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 19 Sep 2025 16:50:32 -0400 Subject: [PATCH 176/235] Fix tests with new pipeline name --- .github/workflows/github-actions.yml | 3 +++ travis_env.sh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 0ecab6d8..218e83c6 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -81,6 +81,9 @@ jobs: ACCESS_HEME_JOB_STORE_ROOT: /sample_path ACCESS_HEME_WORK_DIR_ROOT: /sample_path ACCESS_HEME_TMP_DIR_ROOT: /sample_path + MICROBIOME_JOB_STORE_ROOT: /sample_path + MICROBIOME_WORK_DIR_ROOT: /sample_path + MICROBIOME_TMP_DIR_ROOT: /sample_path DEFAULT_JOB_STORE_ROOT: /sample_path DEFAULT_WORK_DIR_ROOT: /sample_path DEFAULT_TMP_DIR_ROOT: /sample_path diff --git a/travis_env.sh b/travis_env.sh index 262606c6..a0bba53d 100644 --- a/travis_env.sh +++ b/travis_env.sh @@ -35,6 +35,9 @@ export CMO_CH_TMP_DIR_ROOT=/sample_path export ACCESS_HEME_JOB_STORE_ROOT=/sample_path export ACCESS_HEME_WORK_DIR_ROOT=/sample_path export ACCESS_HEME_TMP_DIR_ROOT=/sample_path +export MICROBIOME_JOB_STORE_ROOT=/sample_path +export MICROBIOME_WORK_DIR_ROOT=/sample_path +export MICROBIOME_TMP_DIR_ROOT=/sample_path export DEFAULT_JOB_STORE_ROOT=/sample_path export DEFAULT_WORK_DIR_ROOT=/sample_path export DEFAULT_TMP_DIR_ROOT=/sample_path From e4aa7477f4a8f4091081d6b60c1cf1134eb77e07 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 19 Sep 2025 17:06:13 -0400 Subject: [PATCH 177/235] Fix get job info test --- orchestrator/tests/test_tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/orchestrator/tests/test_tasks.py b/orchestrator/tests/test_tasks.py index acc6b4f2..1f99863c 100644 --- a/orchestrator/tests/test_tasks.py +++ b/orchestrator/tests/test_tasks.py @@ -554,12 +554,13 @@ def test_get_job_info_path(self): } }, external_id="ext_id", + working_dir=f"/toil/work/dir/root", status=Status.SUBMITTED, metadata={"pipeline_name": "TEST"}, ) with self.settings(PIPELINE_CONFIG=PIPELINE_CONFIG): res = get_job_info_path(str(job.id)) - self.assertEqual(res, f"/toil/work/dir/root/{str(job.id)}/.run.info") + self.assertEqual(res, f"{str(job.working_dir)}/.run.info") def test_permission(self): with tempfile.TemporaryDirectory() as temp_path: From fa9a544249af94b31b6de400e85d018f37f4019e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 15:35:50 -0400 Subject: [PATCH 178/235] Add dynamic partition selection --- batch_systems/batch_system.py | 5 +++-- batch_systems/lsf_client/lsf_client.py | 18 +++++++++++++----- batch_systems/slurm_client/slurm_client.py | 11 ++++++----- ridgeback/settings.py | 11 +++++++---- submitter/jobsubmitter.py | 18 ++++++++++++++++++ .../nextflow_jobsubmitter.py | 15 +++------------ submitter/toil_submitter/toil_jobsubmitter.py | 17 ++++------------- 7 files changed, 54 insertions(+), 41 deletions(-) diff --git a/batch_systems/batch_system.py b/batch_systems/batch_system.py index 6488aeff..e84e2e39 100644 --- a/batch_systems/batch_system.py +++ b/batch_systems/batch_system.py @@ -33,7 +33,7 @@ def __init__(self, user=getuser()): self.name = "batch" self.user = user - def submit(self, command, job_args, stdout, job_id, env={}): + def submit(self, command, job_args, stdout, job_id, partition, env={}): """ Submit command to bath system and store log in stdout @@ -42,6 +42,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): job_args (list): Additional options for leader job stdout (str): log file path job_id (str): job_id + partition (str): the batch system partition to use env (dict): Environment variables Returns: @@ -101,7 +102,7 @@ def set_stdout_file(self, stdout_file): """ return [] - def set_service_queue(self): + def set_service_queue(self, partition): """ Set the service queue parameter """ diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index 176caf49..3cf0565a 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -37,7 +37,7 @@ def __init__(self, user=getuser()): self.user = user @userswitch - def submit(self, command, job_args, stdout, job_id, env={}): + def submit(self, command, job_args, stdout, job_id, partition, env={}): """ Submit command to LSF and store log in stdout @@ -46,13 +46,18 @@ def submit(self, command, job_args, stdout, job_id, env={}): job_args (list): Additional options for leader bsub stdout (str): log file path job_id (str): job_id + partition (str): the batch system partition to use env (dict): Environment variables Returns: int: lsf job id """ bsub_command = ( - ["bsub"] + self.set_service_queue() + self.set_group(job_id) + self.set_stdout_file(stdout) + job_args + ["bsub"] + + self.set_service_queue(partition) + + self.set_group(job_id) + + self.set_stdout_file(stdout) + + job_args ) bsub_command.extend(command) current_env = os.environ.copy() @@ -142,10 +147,13 @@ def set_stdout_file(self, stdout_file): else: return ["-oo", self.logfileName] - def set_service_queue(self): + def set_service_queue(self, partition): + """ + Set the service queue parameter + """ service_queue_args = [] - if settings.LSF_SLA: - service_queue_args = ["-sla", settings.LSF_SLA] + if partition: + service_queue_args = ["-sla", partition] return service_queue_args def _parse_bjobs(self, bjobs_output_str): diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index dbe6c7af..1c5df762 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -32,7 +32,7 @@ def __init__(self, user=getuser()): self.user = user @userswitch - def submit(self, command, job_args, stdout, job_id, env={}): + def submit(self, command, job_args, stdout, job_id, partition, env={}): """ Submit command to SLURM and store log in stdout @@ -41,6 +41,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): job_args (list): Additional options for leader sbatch stdout (str): log file path job_id (str): job_id + partition (str): the batch system partition to use env (dict): Environment variables Returns: @@ -52,7 +53,7 @@ def submit(self, command, job_args, stdout, job_id, env={}): sbatch_command = ( ["sbatch"] - + self.set_service_queue() + + self.set_service_queue(partition) + self.set_group(job_id) + self.set_stdout_file(stdout) + job_args @@ -147,10 +148,10 @@ def set_stdout_file(self, stdout_file): return [f"--output={stdout_file}"] return [f"--output={self.logfileName}"] - def set_service_queue(self): + def set_service_queue(self, partition): service_queue_args = [] - if settings.SLURM_PARTITION: - service_queue_args = [f"--partition={settings.SLURM_PARTITION}"] + if partition: + service_queue_args = [f"--partition={partition}"] return service_queue_args def _parse_sacct(self, sacct_output_str, external_job_id): diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 70f8e53d..4276dc26 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -242,46 +242,49 @@ "JOB_STORE_ROOT": os.environ["ARGOS_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["ARGOS_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["ARGOS_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("ARGOS_PARTITION", None), }, "TEMPO": { "JOB_STORE_ROOT": os.environ["TEMPO_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["TEMPO_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["TEMPO_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("TEMPO_PARTITION", None), }, "ACCESS": { "JOB_STORE_ROOT": os.environ["ACCESS_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["ACCESS_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["ACCESS_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("ACCESS_PARTITION", None), }, "CMO-CH": { "JOB_STORE_ROOT": os.environ["CMO_CH_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["CMO_CH_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["CMO_CH_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("CMO_CH_PARTITION", None), }, "ACCESS_HEME": { "JOB_STORE_ROOT": os.environ["ACCESS_HEME_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["ACCESS_HEME_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["ACCESS_HEME_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("ACCESS_HEME_PARTITION", None), }, "MICROBIOME": { "JOB_STORE_ROOT": os.environ["MICROBIOME_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["MICROBIOME_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["MICROBIOME_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("MICROBIOME_PARTITION", None), }, "NA": { "JOB_STORE_ROOT": os.environ["DEFAULT_JOB_STORE_ROOT"], "WORK_DIR_ROOT": os.environ["DEFAULT_WORK_DIR_ROOT"], "TMP_DIR_ROOT": os.environ["DEFAULT_TMP_DIR_ROOT"], + "PARTITION": os.environ.get("DEFAULT_PARTITION", None), }, } # Batch System settings BATCH_SYSTEM = os.environ.get("RIDGEBACK_BATCH_SYSTEM", "LSF") -# SLURM settings - -SLURM_PARTITION = os.environ.get("RIDGEBACK_SLURM_PARTITION", None) - # LSF settings LSF_WALLTIME = os.environ["RIDGEBACK_LSF_WALLTIME"] diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index 7bd319e4..b75f6320 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -4,6 +4,7 @@ from django.conf import settings from submitter.userswitcher import userswitch from getpass import getuser +from batch_systems.batch_system import get_batch_system class JobSubmitter(object): @@ -12,6 +13,8 @@ def __init__( job_id, app, inputs, + root_dir, + resume_jobstore, walltime, tool_walltime, memlimit, @@ -32,6 +35,21 @@ def __init__( self.app_name = app_name self.root_permissions = root_permissions self.user = user + self.pipeline_config = None + self.partition_isolated = None + pipeline_config = settings.PIPELINE_CONFIG.get(self.app_name) + if not self.pipeline_config: + self.pipeline_config = settings.PIPELINE_CONFIG["NA"] + self.resume_jobstore = resume_jobstore + if resume_jobstore: + self.job_store_dir = resume_jobstore + else: + self.job_store_dir = os.path.join(pipeline_config["JOB_STORE_ROOT"], self.job_id) + self.partition = pipeline_config["PARTITION"] + self.job_work_dir = os.path.join(pipeline_config["WORK_DIR_ROOT"], self.job_id) + self.job_outputs_dir = root_dir + self.job_tmp_dir = os.path.join(pipeline_config["TMP_DIR_ROOT"], self.job_id) + self.batch_system = get_batch_system() def prepare_to_submit(self): """ diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 41f7d385..40383714 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -55,6 +55,8 @@ def __init__( job_id, app, inputs, + root_dir, + resume_jobstore, walltime, tool_walltime, memlimit, @@ -64,22 +66,10 @@ def __init__( root_permissions=root_permissions, user=user, ) - self.resume_jobstore = resume_jobstore - dir_config = settings.PIPELINE_CONFIG.get(self.app_name) if self.app.nfcore_template: self.cli_output_name = "--outdir" else: self.cli_output_name = "--outDir" - if not dir_config: - dir_config = settings.PIPELINE_CONFIG["NA"] - if resume_jobstore: - self.job_store_dir = resume_jobstore - else: - self.job_store_dir = os.path.join(dir_config["JOB_STORE_ROOT"], self.job_id) - self.job_work_dir = os.path.join(dir_config["WORK_DIR_ROOT"], self.job_id) - self.job_outputs_dir = root_dir - self.job_tmp_dir = os.path.join(dir_config["TMP_DIR_ROOT"], self.job_id) - self.batch_system = get_batch_system() def prepare_to_submit(self): self._prepare_directories() @@ -96,6 +86,7 @@ def get_submit_command(self): env["PATH"] = env["JAVA_HOME"] + "bin:" + os.environ["PATH"] env["TMPDIR"] = self.job_tmp_dir env["NXF_CACHE_DIR"] = self.job_store_dir + env["NXF_SLURM_PARTITION"] = self.partition return command_line, self._leader_args(), log_path, self.job_id, env def _leader_args(self): diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 6511de5b..a62286dc 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -48,6 +48,8 @@ def __init__( job_id, app, inputs, + root_dir, + resume_jobstore, walltime, tool_walltime, memlimit, @@ -57,18 +59,7 @@ def __init__( root_permissions=root_permissions, user=user, ) - dir_config = settings.PIPELINE_CONFIG.get(self.app_name) - if not dir_config: - dir_config = settings.PIPELINE_CONFIG["NA"] - self.resume_jobstore = resume_jobstore - if resume_jobstore: - self.job_store_dir = resume_jobstore - else: - self.job_store_dir = os.path.join(dir_config["JOB_STORE_ROOT"], self.job_id) - self.job_work_dir = os.path.join(dir_config["WORK_DIR_ROOT"], self.job_id) - self.job_outputs_dir = root_dir - self.job_tmp_dir = os.path.join(dir_config["TMP_DIR_ROOT"], self.job_id) - self.batch_system = get_batch_system() + self.batch_system_args_env = None self.single_machine_mode_workflows = ["nucleo_qc", "argos-qc"] if settings.BATCH_SYSTEM == "LSF": @@ -223,7 +214,7 @@ def _tool_args(self): return args def _service_queue(self): - return self.batch_system.set_service_queue() + return self.partition def _walltime(self): return self.batch_system.set_walltime(None, self.walltime) From 367063f1f6ddf61b80c53d448d445eeb24745301 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 15:36:26 -0400 Subject: [PATCH 179/235] Fixed config true/false parse logic --- ridgeback/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 4276dc26..81c726b7 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -190,7 +190,7 @@ RABBITMQ_USERNAME = os.environ.get("RIDGEBACK_RABBITMQ_USERNAME", "guest") RABBITMQ_PASSWORD = os.environ.get("RIDGEBACK_RABBITMQ_PASSWORD", "guest") RABBITMQ_URL = os.environ.get("RIDGEBACK_RABBITMQ_URL", "localhost") -ENABLE_USER_SWITCH = True if os.environ.get("RIDGEBACK_ENABLE_USER_SWITCH", "True") != "False" else False +ENABLE_USER_SWITCH = True if os.environ.get("RIDGEBACK_ENABLE_USER_SWITCH", "true") != "false" else False CELERY_BROKER_URL = os.environ.get( "CELERY_BROKER_URL", From 1815f68d49437e5604e086f6b15333b793fd9880 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 16:10:14 -0400 Subject: [PATCH 180/235] Fix tests --- submitter/toil_submitter/toil_jobsubmitter.py | 5 ++++- travis_env.sh | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index a62286dc..76004a25 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -214,7 +214,10 @@ def _tool_args(self): return args def _service_queue(self): - return self.partition + service_queue_args = [] + if self.partition: + service_queue_args = ["-sla", self.partition] + return service_queue_args def _walltime(self): return self.batch_system.set_walltime(None, self.walltime) diff --git a/travis_env.sh b/travis_env.sh index a0bba53d..777aef30 100644 --- a/travis_env.sh +++ b/travis_env.sh @@ -23,29 +23,35 @@ export RIDGEBACK_RABBITMQ_PASSWORD=sample_password export ARGOS_JOB_STORE_ROOT=/sample_path export ARGOS_WORK_DIR_ROOT=/sample_path export ARGOS_TMP_DIR_ROOT=/sample_path +export ARGOS_PARTITION=sample_SLA export TEMPO_JOB_STORE_ROOT=/sample_path export TEMPO_WORK_DIR_ROOT=/sample_path export TEMPO_TMP_DIR_ROOT=/sample_path +export TEMPO_PARTITION=sample_SLA export ACCESS_JOB_STORE_ROOT=/sample_path export ACCESS_WORK_DIR_ROOT=/sample_path export ACCESS_TMP_DIR_ROOT=/sample_path +export ACCESS_PARTITION=sample_SLA export CMO_CH_JOB_STORE_ROOT=/sample_path export CMO_CH_WORK_DIR_ROOT=/sample_path export CMO_CH_TMP_DIR_ROOT=/sample_path +export CMO_CH_PARTITION=sample_SLA export ACCESS_HEME_JOB_STORE_ROOT=/sample_path export ACCESS_HEME_WORK_DIR_ROOT=/sample_path export ACCESS_HEME_TMP_DIR_ROOT=/sample_path +export ACCESS_HEME_PARTITION=sample_SLA export MICROBIOME_JOB_STORE_ROOT=/sample_path export MICROBIOME_WORK_DIR_ROOT=/sample_path export MICROBIOME_TMP_DIR_ROOT=/sample_path +export MICROBIOME_PARTITION=sample_SLA export DEFAULT_JOB_STORE_ROOT=/sample_path export DEFAULT_WORK_DIR_ROOT=/sample_path export DEFAULT_TMP_DIR_ROOT=/sample_path +export DEFAULT_PARTITION=sample_SLA ### Set the LSF env variable export RIDGEBACK_LSF_WALLTIME=10:00 -export RIDGEBACK_LSF_SLA=sample_SLA ### Set the celery env variable From d719e4336204b64daf6648d980d2cc98fec079e6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 16:13:00 -0400 Subject: [PATCH 181/235] Updated tests --- .github/workflows/github-actions.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 218e83c6..839d5891 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -46,7 +46,6 @@ jobs: #lsf env variables RIDGEBACK_LSF_WALLTIME: 10:00 - RIDGEBACK_LSF_SLA: SLA #celery env variables CELERY_LOG_PATH: /sample_path @@ -69,21 +68,28 @@ jobs: ARGOS_JOB_STORE_ROOT: /sample_path ARGOS_WORK_DIR_ROOT: /sample_path ARGOS_TMP_DIR_ROOT: /sample_path + ARGOS_PARTITION: sample_SLA TEMPO_JOB_STORE_ROOT: /sample_path TEMPO_WORK_DIR_ROOT: /sample_path TEMPO_TMP_DIR_ROOT: /sample_path + TEMPO_PARTITION: sample_SLA ACCESS_JOB_STORE_ROOT: /sample_path ACCESS_WORK_DIR_ROOT: /sample_path ACCESS_TMP_DIR_ROOT: /sample_path + ACCESS_PARTITION: sample_SLA CMO_CH_JOB_STORE_ROOT: /sample_path CMO_CH_WORK_DIR_ROOT: /sample_path CMO_CH_TMP_DIR_ROOT: /sample_path + CMO_CH_PARTITION: sample_SLA ACCESS_HEME_JOB_STORE_ROOT: /sample_path ACCESS_HEME_WORK_DIR_ROOT: /sample_path ACCESS_HEME_TMP_DIR_ROOT: /sample_path + ACCESS_HEME_PARTITION: sample_SLA MICROBIOME_JOB_STORE_ROOT: /sample_path MICROBIOME_WORK_DIR_ROOT: /sample_path MICROBIOME_TMP_DIR_ROOT: /sample_path + MICROBIOME_PARTITION: sample_SLA DEFAULT_JOB_STORE_ROOT: /sample_path DEFAULT_WORK_DIR_ROOT: /sample_path DEFAULT_TMP_DIR_ROOT: /sample_path + DEFAULT_PARTITION: sample_SLA From 0528ffa9bffabe44eb979811e962525a2b8b310e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 18:42:21 -0400 Subject: [PATCH 182/235] Fixed tests --- submitter/jobsubmitter.py | 4 ++-- tests/test_commandline.py | 7 +++++- tests/test_lsf_client.py | 4 ++-- tests/test_slurm_client.py | 47 +++++++++++++++++++------------------- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index b75f6320..9e538751 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -38,8 +38,8 @@ def __init__( self.pipeline_config = None self.partition_isolated = None pipeline_config = settings.PIPELINE_CONFIG.get(self.app_name) - if not self.pipeline_config: - self.pipeline_config = settings.PIPELINE_CONFIG["NA"] + if not pipeline_config: + pipeline_config = settings.PIPELINE_CONFIG["NA"] self.resume_jobstore = resume_jobstore if resume_jobstore: self.job_store_dir = resume_jobstore diff --git a/tests/test_commandline.py b/tests/test_commandline.py index 70b5dea8..38b92571 100644 --- a/tests/test_commandline.py +++ b/tests/test_commandline.py @@ -90,7 +90,12 @@ def check_status(self, jobstore, work_dir, tmp_dir, bus_path, get_bus_path): copytree(work_dir, new_work_dir) with override_settings( PIPELINE_CONFIG={ - "NA": {"JOB_STORE_ROOT": tmp_jobstore, "WORK_DIR_ROOT": tmp_work_dir, "TMP_DIR_ROOT": "/tmp"} + "NA": { + "JOB_STORE_ROOT": tmp_jobstore, + "WORK_DIR_ROOT": tmp_work_dir, + "TMP_DIR_ROOT": "/tmp", + "PARTITION": "test_partition", + } } ): check_status_of_command_line_jobs(self.job) diff --git a/tests/test_lsf_client.py b/tests/test_lsf_client.py index 12e24132..290ee972 100644 --- a/tests/test_lsf_client.py +++ b/tests/test_lsf_client.py @@ -68,7 +68,7 @@ def test_submit(self, submit_process): submit_process_obj.stdout = self.submit_response submit_process_obj.returncode = 0 submit_process.return_value = submit_process_obj - lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, {}) + lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, settings.LSF_SLA, {}) expected_command = ( ["bsub", "-sla", settings.LSF_SLA, "-g", self.example_lsf_id, "-oo", stdout_file] + args + command ) @@ -87,7 +87,7 @@ def test_submit_slow_lsf(self, submit_process): submit_process_obj.stdout = self.submit_response_please_wait submit_process_obj.returncode = 0 submit_process.return_value = submit_process_obj - lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, {}) + lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, settings.LSF_SLA, {}) self.assertEqual(lsf_id, self.example_id) @patch("subprocess.run") diff --git a/tests/test_slurm_client.py b/tests/test_slurm_client.py index ce005f98..3ca39669 100644 --- a/tests/test_slurm_client.py +++ b/tests/test_slurm_client.py @@ -49,18 +49,17 @@ def test_submit(self, submit_process): submit_process_obj.stdout = self.submit_response submit_process_obj.returncode = 0 submit_process.return_value = submit_process_obj - with self.settings(SLURM_PARTITION=self.example_partion): - slurm_id = self.slurm_client.submit([command], args, stdout_file, self.example_job_id, {}) - expected_command = ( - [ - "sbatch", - f"--partition={self.example_partion}", - f"--wckey={self.example_job_id}", - f"--output={self.slurm_client.logfileName}", - ] - + args - + [f"--wrap=exec {command}"] - ) + slurm_id = self.slurm_client.submit([command], args, stdout_file, self.example_job_id, self.example_partion, {}) + expected_command = ( + [ + "sbatch", + f"--partition={self.example_partion}", + f"--wckey={self.example_job_id}", + f"--output={self.slurm_client.logfileName}", + ] + + args + + [f"--wrap=exec {command}"] + ) self.assertEqual(f"{slurm_id}", self.example_id) self.assertEqual(submit_process.call_args[0][0], expected_command) @@ -80,18 +79,18 @@ def test_submit_with_args(self, submit_process): mem_limit = 8 args = self.slurm_client.set_walltime(expected_limit, None) args.extend(self.slurm_client.set_memlimit(mem_limit)) - with self.settings(SLURM_PARTITION=self.example_partion): - slurm_id = self.slurm_client.submit([command], args, stdout_file, self.example_job_id, {}) - expected_command = ( - [ - "sbatch", - f"--partition={self.example_partion}", - f"--wckey={self.example_job_id}", - f"--output={self.slurm_client.logfileName}", - ] - + args - + [f"--wrap=exec {command}"] - ) + + slurm_id = self.slurm_client.submit([command], args, stdout_file, self.example_job_id, self.example_partion, {}) + expected_command = ( + [ + "sbatch", + f"--partition={self.example_partion}", + f"--wckey={self.example_job_id}", + f"--output={self.slurm_client.logfileName}", + ] + + args + + [f"--wrap=exec {command}"] + ) self.assertEqual(f"{slurm_id}", self.example_id) self.assertEqual(submit_process.call_args[0][0], expected_command) From c5808cf30c32305682a5f0a830c6cebb3cf176e2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 18:46:46 -0400 Subject: [PATCH 183/235] Fixed LSF test --- tests/test_lsf_client.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/test_lsf_client.py b/tests/test_lsf_client.py index 290ee972..be44e0c4 100644 --- a/tests/test_lsf_client.py +++ b/tests/test_lsf_client.py @@ -68,10 +68,26 @@ def test_submit(self, submit_process): submit_process_obj.stdout = self.submit_response submit_process_obj.returncode = 0 submit_process.return_value = submit_process_obj - lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, settings.LSF_SLA, {}) - expected_command = ( - ["bsub", "-sla", settings.LSF_SLA, "-g", self.example_lsf_id, "-oo", stdout_file] + args + command - ) + partition = "test_partition" + lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, partition, {}) + expected_command = ["bsub", "-sla", partition, "-g", self.example_lsf_id, "-oo", stdout_file] + args + command + self.assertEqual(lsf_id, self.example_id) + self.assertEqual(submit_process.call_args[0][0], expected_command) + + @patch("subprocess.run") + def test_submit_no_sla(self, submit_process): + """ + Test LSF submit with no sla + """ + command = ["ls"] + args = [] + stdout_file = "stdout.txt" + submit_process_obj = Mock() + submit_process_obj.stdout = self.submit_response + submit_process_obj.returncode = 0 + submit_process.return_value = submit_process_obj + lsf_id = self.lsf_client.submit(command, args, stdout_file, self.example_job_id, {}) + expected_command = ["bsub", "-g", self.example_lsf_id, "-oo", stdout_file] + args + command self.assertEqual(lsf_id, self.example_id) self.assertEqual(submit_process.call_args[0][0], expected_command) From 7fd464f73aeda39c09d1f0996ba904a89a812181 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 14 Oct 2025 18:51:21 -0400 Subject: [PATCH 184/235] Fixed flake tests --- batch_systems/lsf_client/lsf_client.py | 1 - batch_systems/slurm_client/slurm_client.py | 1 - orchestrator/tests/test_tasks.py | 2 +- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 1 - submitter/toil_submitter/toil_jobsubmitter.py | 1 - submitter/userswitcher.py | 2 +- 6 files changed, 2 insertions(+), 6 deletions(-) diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index 3cf0565a..e877dbbe 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -7,7 +7,6 @@ import subprocess import json import logging -from django.conf import settings from orchestrator.models import Status from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException from batch_systems.batch_system import BatchClient diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 1c5df762..8ccb3e5c 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -6,7 +6,6 @@ import re import subprocess import logging -from django.conf import settings from orchestrator.models import Status from orchestrator.exceptions import FailToSubmitToSchedulerException, FetchStatusException from batch_systems.batch_system import BatchClient diff --git a/orchestrator/tests/test_tasks.py b/orchestrator/tests/test_tasks.py index 1f99863c..226e863a 100644 --- a/orchestrator/tests/test_tasks.py +++ b/orchestrator/tests/test_tasks.py @@ -554,7 +554,7 @@ def test_get_job_info_path(self): } }, external_id="ext_id", - working_dir=f"/toil/work/dir/root", + working_dir="/toil/work/dir/root", status=Status.SUBMITTED, metadata={"pipeline_name": "TEST"}, ) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 40383714..4ebe1e6f 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -3,7 +3,6 @@ import json from django.conf import settings from submitter import JobSubmitter -from batch_systems.batch_system import get_batch_system from submitter.userswitcher import userswitch from getpass import getuser diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 76004a25..31a2b57e 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -7,7 +7,6 @@ from orchestrator.models import Status from submitter import JobSubmitter from .toil_track_utils import ToilTrack, ToolStatus -from batch_systems.batch_system import get_batch_system from submitter.userswitcher import userswitch from getpass import getuser diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 53e3e6b0..e625d9ee 100644 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -83,7 +83,7 @@ def dzdo_wrapper(*args, **kwargs): output, stdout = dill.loads(e.output) if stdout: stdout_str = stdout.decode().strip() - except Exception as error: + except Exception: stdout_str = "NA" exception_message = f""" Error while userswitching: From 16ebdc9497725d3971645cfeee12ac75b5c6c2f5 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 15 Oct 2025 08:56:09 -0400 Subject: [PATCH 185/235] Made userswitcher executable by any user --- submitter/userswitcher.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 submitter/userswitcher.py diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py old mode 100644 new mode 100755 From eb9e1207d8fdee7220cb5a0e7a872cdc2f33b420 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 15 Oct 2025 08:56:36 -0400 Subject: [PATCH 186/235] Fixed tests --- orchestrator/tasks.py | 4 ++-- submitter/jobsubmitter.py | 2 +- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 2 +- submitter/toil_submitter/toil_jobsubmitter.py | 7 ++----- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 7f2a99f5..530db325 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -238,8 +238,8 @@ def submit_job_to_batch_system(job, retries=0): user=job.user, ) try: - command_line, args, log_path, job_id, env = submitter.get_submit_command() - external_job_id = get_batch_system(job.user).submit(command_line, args, log_path, job_id, env) + command_line, args, log_path, job_id, partition, env = submitter.get_submit_command() + external_job_id = get_batch_system(job.user).submit(command_line, args, log_path, job_id, partition, env) except Exception as f: if retries < 5: logger.exception(str(f)) diff --git a/submitter/jobsubmitter.py b/submitter/jobsubmitter.py index 9e538751..21d54440 100644 --- a/submitter/jobsubmitter.py +++ b/submitter/jobsubmitter.py @@ -59,7 +59,7 @@ def prepare_to_submit(self): def get_submit_command(self): """ - return: command_line, args, log_path, job_id, env_map + return: command_line, args, log_path, job_id, partition, env_map """ pass diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 4ebe1e6f..345df213 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -86,7 +86,7 @@ def get_submit_command(self): env["TMPDIR"] = self.job_tmp_dir env["NXF_CACHE_DIR"] = self.job_store_dir env["NXF_SLURM_PARTITION"] = self.partition - return command_line, self._leader_args(), log_path, self.job_id, env + return command_line, self._leader_args(), log_path, self.job_id, self.partition, env def _leader_args(self): args = self._walltime() diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 31a2b57e..c50fcbaa 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -92,7 +92,7 @@ def get_submit_command(self): env[self.batch_system_args_env] = " ".join( [env[self.batch_system_args_env], self.batch_system.get_env_export_flag()] ) - return command_line, self._leader_args(), log_path, self.job_id, env + return command_line, self._leader_args(), log_path, self.job_id, self.partition, env def get_commandline_status(self, cache): """ @@ -213,10 +213,7 @@ def _tool_args(self): return args def _service_queue(self): - service_queue_args = [] - if self.partition: - service_queue_args = ["-sla", self.partition] - return service_queue_args + return self.batch_system.set_service_queue(self.partition) def _walltime(self): return self.batch_system.set_walltime(None, self.walltime) From b37fc6c864e1756a576b171c7a340950630a4dc2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 15 Oct 2025 09:37:53 -0400 Subject: [PATCH 187/235] Set toil tmpdir --- submitter/toil_submitter/toil_jobsubmitter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index c50fcbaa..f0f2890b 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -86,6 +86,8 @@ def get_submit_command(self): " ".join(self._tool_args()), ) env["JAVA_HOME"] = None + env["TMP"] = self.job_tmp_dir + env["TMPDIR"] = self.job_tmp_dir env[self.batch_system_args_env] = toil_batch_system_args.strip() if settings.ACCESS_LEGACY_APP in self.app.github.lower(): env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) From 846eb5b97ca0be4d2f2a5a28c861b4b8d681ae1b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 15 Oct 2025 11:14:00 -0400 Subject: [PATCH 188/235] Handle empty extension --- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 345df213..36717e05 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -214,7 +214,9 @@ def _dump_app_inputs(self): self._dump_config(config) def _dump_input(self, name, content, extension, root_dir): - file_path = os.path.join(root_dir, name) + extension + file_path = os.path.join(root_dir, name) + if extension: + file_path = file_path + extension with open(file_path, "w") as f: f.write(content) return file_path From 4d4dcb5cdda9bbcf1aaf71a7b4853471827a7dad Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 15 Oct 2025 13:48:41 -0400 Subject: [PATCH 189/235] Fix more edge cases on extension --- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 36717e05..2e892b64 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -195,7 +195,11 @@ def inputs_location(self): inputs = self.inputs.get("inputs", []) input_map = dict() for i in inputs: - input_map[i["name"]] = os.path.join(self.job_work_dir, i["name"]) + i.get("extension", "") + file_path = os.path.join(self.job_work_dir, i["name"]) + extension = i.get("extension", "") + if extension: + file_path = file_path + extension + input_map[i["name"]] = file_path return input_map @property From 89937afeb7e3382119acd7c1a884118a02ebd4ed Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 15 Oct 2025 16:50:17 -0400 Subject: [PATCH 190/235] Handle optional config --- submitter/nextflow_submitter/nextflow_jobsubmitter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/submitter/nextflow_submitter/nextflow_jobsubmitter.py b/submitter/nextflow_submitter/nextflow_jobsubmitter.py index 2e892b64..73a112ce 100755 --- a/submitter/nextflow_submitter/nextflow_jobsubmitter.py +++ b/submitter/nextflow_submitter/nextflow_jobsubmitter.py @@ -233,6 +233,7 @@ def _dump_config(self, config): def _command_line(self): profile = self.inputs["profile"] + config = self.inputs.get("config") if not profile: profile = "''" params = self.inputs.get("params", {}) @@ -251,7 +252,7 @@ def _command_line(self): ] for k, v in self.inputs_location.items(): command_line.extend(["--%s" % k, v]) - if self.config_location: + if config: command_line.extend(["-c", self.config_location]) if params: for k, v in params.items(): From 3769db47224e130132a6315d1d753a9b46c26ad2 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:13:14 -0400 Subject: [PATCH 191/235] Removed code from image --- Dockerfile | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2d92a0ca..764ac56e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,28 +10,15 @@ LABEL org.opencontainers.image.vendor="MSKCC" \ org.opencontainers.image.description="API for running Toil and Nextflow jobs, supports LSF, SLURM, and singleMachine mode" ENV DEBIAN_FRONTEND noninteractive -ENV PIP_ROOT_USER_ACTION ignore -ENV PIP_BREAK_SYSTEM_PACKAGES 1 -ENV RIDGEBACK_BRANCH feature/IRIS_update RUN apt-get update \ # Install dependencies && apt-get -y --no-install-recommends install \ wget curl libldap2-dev libsasl2-dev procps libssl-dev libxml2-dev libxslt-dev \ libpq-dev gawk nodejs git build-essential openssh-client \ - # Install Ridgeback - && git clone https://github.com/mskcc/ridgeback --branch $RIDGEBACK_BRANCH /usr/bin/ridgeback\ # Install alternative ssl library && wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ && dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ - # Install libffi6 for TOIL - && wget http://archive.ubuntu.com/ubuntu/pool/main/libf/libffi/libffi6_3.2.1-8_amd64.deb \ - && dpkg -i libffi6_3.2.1-8_amd64.deb \ - # Install python packages - && pip3 install --upgrade pip \ - && pip3 install python-ldap \ - && pip3 install --use-pep517 -r /usr/bin/ridgeback/requirements.txt \ - && pip3 install --use-pep517 -r /usr/bin/ridgeback/requirements-toil.txt \ # Clean up image && rm -rf /var/lib/apt/lists/* From 69c5dbd4befa06bf094c853ff490eb19b83e27cf Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:14:40 -0400 Subject: [PATCH 192/235] Fixed memcached image --- compose.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index 88543614..0bea168d 100644 --- a/compose.yaml +++ b/compose.yaml @@ -212,13 +212,14 @@ services: depends_on: - ridgeback_create_volumes ridgeback_memcached: - image: bitnami/memcached:1.6.37 + image: mskcc/memcached-nc:1.6.39 restart: on-failure user: "${DOCKER_UID}:${DOCKER_GID}" networks: - voyager_net expose: - "11211" + command: ["memcached"] healthcheck: test: ["CMD", "nc", "-z", "localhost", "11211"] interval: 30s From 9580759c57ad298492fdeb7b3f791e3b88adee0a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:15:10 -0400 Subject: [PATCH 193/235] Setup dependency volume handling --- compose.yaml | 81 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/compose.yaml b/compose.yaml index 0bea168d..06fb42d8 100644 --- a/compose.yaml +++ b/compose.yaml @@ -44,12 +44,10 @@ x-ridgeback_celery: &ridgeback_celery - PATH=${PATH}:/usr/batchsystem/bin volumes: - ${SLURM_BIN_PATH}:/usr/batchsystem/bin - - ./logs/:/ridgeback/celery/logs/ - - ./celery/:/ridgeback/celery/ + - logs_path:/ridgeback/celery/logs/ + - celery_path:/ridgeback/celery/ - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - ${CLUSTER_SCRATCH_MOUNT}:${CLUSTER_SCRATCH_MOUNT} - - ${CLUSTER_FILESYSTEM_SHARE_MOUNT}:${CLUSTER_FILESYSTEM_SHARE_MOUNT} - - ${CLUSTER_FILESYSTEM_COLLAB}:${CLUSTER_FILESYSTEM_COLLAB} - ${CLUSTER_FILESYSTEM_TEST}:${CLUSTER_FILESYSTEM_TEST} - ${CLUSTER_PYTHON_MOUNT}:${CLUSTER_PYTHON_MOUNT} - ${CLUSTER_PYTHON_LIBSO_MOUNT}:${CONTAINER_PYTHON_LIBSO_MOUNT} @@ -133,13 +131,13 @@ services: image: alpine:3.8 restart: no volumes: - - ./postgres:/postgres - - ./logs:/logs - - ./celery:/celery - - ./rabbitmq:/rabbitmq - - ./server/:/server - - ./logrotate/:/logrotate - - ${DB_BACKUP_PATH}:/db_backup + - postgres_path:/postgres + - logs_path:/logs + - celery_path:/celery + - rabbitmq_path:/rabbitmq + - server_path:/server + - logrotate_path:/logrotate + - db_backup_path:/db_backup entrypoint: ["/bin/sh", "-c"] command: - | @@ -158,7 +156,7 @@ services: networks: - voyager_net volumes: - - ./postgres/:/var/lib/postgresql/data/ + - postgres_path:/var/lib/postgresql/data/ environment: - POSTGRES_USER=${RIDGEBACK_DB_USERNAME} - POSTGRES_PASSWORD=${RIDGEBACK_DB_PASSWORD} @@ -232,8 +230,8 @@ services: networks: - voyager_net volumes: - - ./rabbitmq/:/var/lib/rabbitmq/ - - ./logs/:/var/log/rabbitmq/ + - rabbitmq_path:/var/lib/rabbitmq/ + - logs_path:/var/log/rabbitmq/ ports: - ${RIDGEBACK_RABBITMQ_MANAGEMENT_PORT}:15672 expose: @@ -269,8 +267,8 @@ services: - RIDGEBACK_RABBITMQ_PORT=5672 - RIDGEBACK_LOG_PATH=/ridgeback/server/django_server.log volumes: - - ./logs/:/ridgeback/server/ - - ./server/:/ridgeback_staticfiles/ + - logs_path:/ridgeback/server/ + - server_path:/ridgeback_staticfiles/ - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - ${CLUSTER_CODE_PATH:-/dev/null}:${CLUSTER_CODE_PATH:-/dev/null} ports: @@ -503,8 +501,8 @@ services: networks: - voyager_net volumes: - - ./logs:/logs - - ./logrotate/:/logrotate + - logs_path:/logs + - logrotate_path:/logrotate entrypoint: ["/bin/sh", "-c"] command: - | @@ -544,8 +542,8 @@ services: networks: - voyager_net volumes: - - ./logs:/logs - - ${DB_BACKUP_PATH}:/db_backup + - logs_path:/logs + - db_backup_path:/db_backup environment: - PGPASSWORD=${RIDGEBACK_DB_PASSWORD} entrypoint: ["/bin/sh", "-c"] @@ -568,6 +566,49 @@ services: ridgeback_celery_command_queue: condition: service_healthy restart: false +volumes: + postgres_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_POSTGRES_PATH}" + logs_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_LOGS_PATH}" + celery_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_CELERY_PATH}" + rabbitmq_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_RABITMQ_PATH}" + server_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_SERVER_PATH}" + logrotate_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_LOGROTATE_PATH}" + db_backup_path: + driver: local + driver_opts: + type: "none" + o: "bind" + device: "${RIDGEBACK_DB_BACKUP_PATH}" networks: voyager_net: name: voyager_network_${RIDGEBACK_DEPLOYMENT} From cb92502812fbf83c3cc5fe44f3f0c6b4654a6a50 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:16:35 -0400 Subject: [PATCH 194/235] Switch database setup service --- compose.yaml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/compose.yaml b/compose.yaml index 06fb42d8..7b966fd9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -291,6 +291,18 @@ services: entrypoint: ["/bin/bash", "-c"] command: - | + source ${RIDGEBACK_VENV}/bin/activate + + if ! python3 -c "import toil" 2>&1 >/dev/null + then + pip3 install --upgrade pip && + pip3 install --force-reinstall 'setuptools<58.0.0' && + pip3 install "cython<3.0.0" wheel && + pip3 install "pyyaml==5.4.1" --no-build-isolation && + pip3 install --use-pep517 -r ${RIDGEBACK_PATH}/requirements.txt && + pip3 install --use-pep517 -r ${RIDGEBACK_PATH}/requirements-toil.txt + fi + python3 ${RIDGEBACK_PATH}/manage.py migrate --noinput echo "User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin','voyager@mskcc.org','${RIDGEBACK_DB_PASSWORD}')" | python3 ${RIDGEBACK_PATH}/manage.py shell_plus python3 ${RIDGEBACK_PATH}/manage.py collectstatic --noinput @@ -317,16 +329,6 @@ services: - | source ${RIDGEBACK_VENV}/bin/activate - if ! python3 -c "import toil" 2>&1 >/dev/null - then - pip3 install --upgrade pip && - pip3 install --force-reinstall 'setuptools<58.0.0' && - pip3 install "cython<3.0.0" wheel && - pip3 install "pyyaml==5.4.1" --no-build-isolation && - pip3 install --use-pep517 -r ${RIDGEBACK_PATH}/requirements.txt && - pip3 install --use-pep517 -r ${RIDGEBACK_PATH}/requirements-toil.txt - fi - PIDFILE=/ridgeback/celery/ridgeback.${RIDGEBACK_DEPLOYMENT}.ridgeback_beat.pid [ -e $$PIDFILE ] && rm $$PIDFILE @@ -357,6 +359,9 @@ services: ridgeback_rabbitmq: condition: service_healthy restart: false + ridgeback: + condition: service_healthy + restart: false ridgeback_celery_command_queue: <<: *ridgeback_celery command: From 1b488f05954bfa6fb962da4904f56d19aca1599b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:16:56 -0400 Subject: [PATCH 195/235] Add tmpfs to some celery tasks --- compose.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compose.yaml b/compose.yaml index 7b966fd9..8853671c 100644 --- a/compose.yaml +++ b/compose.yaml @@ -364,6 +364,8 @@ services: restart: false ridgeback_celery_command_queue: <<: *ridgeback_celery + tmpfs: + - /tmp:size=500M,mode=777 command: - | source ${RIDGEBACK_VENV}/bin/activate @@ -387,6 +389,8 @@ services: retries: 3 ridgeback_celery_action_queue: <<: *ridgeback_celery + tmpfs: + - /tmp:size=500M,mode=777 command: - | source ${RIDGEBACK_VENV}/bin/activate From 025ef82a588f28ca108ed6780ffa6bc1348efe2c Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:17:38 -0400 Subject: [PATCH 196/235] Remove chown as thats no longer supported in IRIS --- orchestrator/tasks.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 530db325..eed568d9 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -484,8 +484,6 @@ def set_permission(job): failed_to_set = None dirs = job.root_dir.replace(job.base_dir, "").split("/") permission_str = job.root_permission - uid = job.output_uid - gid = job.output_gid permissions_dir = job.base_dir for d in dirs: failed_to_set = False @@ -502,13 +500,11 @@ def set_permission(job): logger.debug(f"Setting permissions for {os.path.join(root, single_dir)}") path = os.path.join(root, single_dir) os.chmod(path, permission_octal) - os.chown(path, uid=uid, gid=gid) for single_file in files: if oct(os.lstat(os.path.join(root, single_file)).st_mode)[-3:] != permission_octal: path = os.path.join(root, single_file) logger.debug(f"Setting permissions for {path}") os.chmod(path, permission_octal) - os.chown(path, uid=uid, gid=gid) except Exception: logger.error(f"Failed to set permissions for directory {permissions_dir}") failed_to_set = True From 52b3caefab76d5d0620f09e6f22c311ca4a78250 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:17:55 -0400 Subject: [PATCH 197/235] Log execption with error --- orchestrator/tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index eed568d9..67016e12 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -506,7 +506,7 @@ def set_permission(job): logger.debug(f"Setting permissions for {path}") os.chmod(path, permission_octal) except Exception: - logger.error(f"Failed to set permissions for directory {permissions_dir}") + logger.exception(f"Failed to set permissions for directory {permissions_dir}") failed_to_set = True continue else: From 50420ef0e79abebb21b5049ab624b84a8b0a2124 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:18:32 -0400 Subject: [PATCH 198/235] Decode and encode env for userswitch handling --- submitter/userswitcher.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index e625d9ee..c1b06d27 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -10,6 +10,7 @@ from getpass import getuser import django import json +import zlib from django.conf import settings log = logging.getLogger(__name__) @@ -26,7 +27,8 @@ def userscript(): output = None with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): try: - env_str = sys.argv[1] + env_str_encode = sys.argv[1] + env_str = zlib.decompress(env_str_encode).decode() env_json = json.loads(env_str) for single_env in env_json: if single_env == "PATH": @@ -62,8 +64,9 @@ def dzdo_wrapper(*args, **kwargs): try: job_func = dill.dumps((func, args, kwargs)) env_str = json.dumps(current_env) + env_str_encode = zlib.compress(env_str.encode()) dzdo_process = subprocess.run( - proc_command + [env_str], input=job_func, check=True, capture_output=True, env=current_env + proc_command + [env_str_encode], input=job_func, check=True, capture_output=True, env=current_env ) output, stdout = dill.loads(dzdo_process.stdout) func_stdout = stdout.decode().strip() From 3bb3b751a4d56efe2ba78d81d57e3b60a8d1e795 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:18:52 -0400 Subject: [PATCH 199/235] Code is no longer shipped with the image --- submitter/userswitcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index c1b06d27..3ac2c2bd 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -18,7 +18,7 @@ def userscript(): - ridgeback_path = os.environ.get("RIDGEBACK_PATH", "/usr/bin/ridgeback") + ridgeback_path = os.environ.get("RIDGEBACK_PATH") sys.path.append(ridgeback_path) os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ridgeback.settings") stdout_buffer = io.StringIO() From 4054e3e77b756e7ed41ad47da58a4e7d86928632 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:19:29 -0400 Subject: [PATCH 200/235] Better handling or memory for toil leader jobs --- submitter/toil_submitter/toil_jobsubmitter.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index f0f2890b..5e933b76 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -201,9 +201,11 @@ def _dump_app_inputs(self): def _leader_args(self): single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) args = self._walltime() - args.extend(self._memlimit()) if single_machine: args.extend(self._numtasks(int(settings.SINGLE_MACHINE_CORES))) + if not self.memlimit: + self.memlimit = settings.SINGLE_MACHINE_MEMORY + args.extend(self._memlimit()) return args def _tool_args(self): @@ -221,7 +223,7 @@ def _walltime(self): return self.batch_system.set_walltime(None, self.walltime) def _memlimit(self): - return self.batch_system.set_memlimit(self.memlimit) + return self.batch_system.set_memlimit(self.memlimit, default="5") def _numtasks(self, num_tasks): return self.batch_system.set_num_tasks(num_tasks) From 024c1656bd412ad531cb1e570a7112b7610e043a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:19:55 -0400 Subject: [PATCH 201/235] Set default memory for single machine toil jobs --- ridgeback/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 81c726b7..ec7fa27e 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -297,6 +297,7 @@ TOIL_MAX_CORES = os.environ.get("RIDGEBACK_TOIL_MAX_CORES", "40") TOIL_DEFAULT_MEMORY = os.environ.get("RIDGEBACK_TOIL_DEFAULT_MEMORY", "8G") SINGLE_MACHINE_CORES = os.environ.get("RIDGEBACK_SINGLE_MACHINE_CORES", 16) +SINGLE_MACHINE_MEMORY = os.environ.get("RIDGEBACK_SINGLE_MACHINE_MEMORY", 25) # Nextflow settings From 025c0a3b75f4c4f8cd8b69a735e4201b46e8a55e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:22:08 -0400 Subject: [PATCH 202/235] Major version bump --- ridgeback/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/__init__.py b/ridgeback/__init__.py index c53ad1ad..8c0d5d5b 100644 --- a/ridgeback/__init__.py +++ b/ridgeback/__init__.py @@ -1 +1 @@ -__version__ = "1.40.0" +__version__ = "2.0.0" From ed236712f2d60b7cc9fa74b3043aca401ec4c416 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 16:43:44 -0400 Subject: [PATCH 203/235] Removed test mount --- compose.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index 8853671c..3aeb14a5 100644 --- a/compose.yaml +++ b/compose.yaml @@ -48,7 +48,6 @@ x-ridgeback_celery: &ridgeback_celery - celery_path:/ridgeback/celery/ - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - ${CLUSTER_SCRATCH_MOUNT}:${CLUSTER_SCRATCH_MOUNT} - - ${CLUSTER_FILESYSTEM_TEST}:${CLUSTER_FILESYSTEM_TEST} - ${CLUSTER_PYTHON_MOUNT}:${CLUSTER_PYTHON_MOUNT} - ${CLUSTER_PYTHON_LIBSO_MOUNT}:${CONTAINER_PYTHON_LIBSO_MOUNT} - ${CLUSTER_PYTHON_LIB_MOUNT}:${CLUSTER_PYTHON_LIB_MOUNT} From 79ebf28c8122e2140fac4877c10c53bcdf5ab26f Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 17:23:38 -0400 Subject: [PATCH 204/235] Fixed memlimit handling --- batch_systems/lsf_client/lsf_client.py | 6 +++--- batch_systems/slurm_client/slurm_client.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/batch_systems/lsf_client/lsf_client.py b/batch_systems/lsf_client/lsf_client.py index e877dbbe..49b572ac 100644 --- a/batch_systems/lsf_client/lsf_client.py +++ b/batch_systems/lsf_client/lsf_client.py @@ -108,10 +108,10 @@ def set_walltime(self, expected_limit, hard_limit): def set_memlimit(self, mem_limit, default=None): mem_limit_args = [] - if default: - mem_limit = ["-M", default] if mem_limit: - mem_limit_args = ["-M", mem_limit] + return ["-M", mem_limit] + if default: + mem_limit_args = ["-M", default] return mem_limit_args def set_num_tasks(self, num_tasks, default=None): diff --git a/batch_systems/slurm_client/slurm_client.py b/batch_systems/slurm_client/slurm_client.py index 8ccb3e5c..0ed76d3a 100644 --- a/batch_systems/slurm_client/slurm_client.py +++ b/batch_systems/slurm_client/slurm_client.py @@ -110,10 +110,10 @@ def set_walltime(self, expected_limit, hard_limit): def set_memlimit(self, mem_limit, default=None): mem_limit_args = [] - if default: - return [f"--mem={default}G"] if mem_limit: - mem_limit_args = [f"--mem={mem_limit}G"] + return [f"--mem={mem_limit}G"] + if default: + mem_limit_args = [f"--mem={default}G"] return mem_limit_args def set_num_tasks(self, num_tasks, default=None): From 07c07b20643e318a47264b9c460d2c586fc353c6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 31 Oct 2025 17:23:51 -0400 Subject: [PATCH 205/235] Updated tests --- tests/test_tasks.py | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/test_tasks.py b/tests/test_tasks.py index b6f6440b..c953261c 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -16,6 +16,7 @@ from submitter.toil_submitter import ToilJobSubmitter MAX_RUNNING_JOBS = 3 +DEFAULT_MEMLIMIT = 5 class TestTasks(TestCase): @@ -148,7 +149,7 @@ def test_job_args_walltime_lsf(self): tool_walltime = 24 memlimit = None inputs = {} - expected_job_args = "-W {}".format(walltime) + expected_job_args = "-W {} -M {}".format(walltime, DEFAULT_MEMLIMIT) jobsubmitterObject = ToilJobSubmitter( job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit ) @@ -166,7 +167,7 @@ def test_job_args_walltime_slurm(self): tool_walltime = 24 memlimit = None inputs = {} - expected_job_args = "--time={}".format(walltime) + expected_job_args = "--time={} --mem={}G".format(walltime, DEFAULT_MEMLIMIT) jobsubmitterObject = ToilJobSubmitter( job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit ) @@ -213,6 +214,24 @@ def test_job_args_tool_walltime_slurm(self): tool_args = " ".join([str(single_arg) for single_arg in tool_args_list]) self.assertEqual(tool_args, expected_tool_args) + def test_job_args_default_memlimit_lsf(self): + with override_settings(BATCH_SYSTEM="LSF"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = None + tool_walltime = None + memlimit = None + inputs = {} + expected_leader_args = "-M {}".format(DEFAULT_MEMLIMIT) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + self.assertEqual(leader_args, expected_leader_args) + def test_job_args_memlimit_lsf(self): with override_settings(BATCH_SYSTEM="LSF"): job_id = str(uuid.uuid4()) @@ -249,6 +268,24 @@ def test_job_args_memlimit_slurm(self): leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) self.assertEqual(leader_args, expected_leader_args) + def test_job_args_default_memlimit_slurm(self): + with override_settings(BATCH_SYSTEM="SLURM"): + job_id = str(uuid.uuid4()) + app = {"github": {"repository": "awesome_repo", "entrypoint": "test.cwl"}} + root_dir = "test_root" + resume_jobstore = None + walltime = None + tool_walltime = None + memlimit = None + inputs = {} + expected_leader_args = "--mem={}G".format(DEFAULT_MEMLIMIT) + jobsubmitterObject = ToilJobSubmitter( + job_id, app, inputs, root_dir, resume_jobstore, walltime, tool_walltime, memlimit + ) + leader_args_list = jobsubmitterObject._leader_args() + leader_args = " ".join([str(single_arg) for single_arg in leader_args_list]) + self.assertEqual(leader_args, expected_leader_args) + def test_job_args_all_options_lsf(self): with override_settings(BATCH_SYSTEM="LSF"): job_id = str(uuid.uuid4()) From d094dab04b50bd95bc4722f0d650d7de1a614dd2 Mon Sep 17 00:00:00 2001 From: Allan Bolipata Date: Sun, 2 Nov 2025 01:08:27 -0400 Subject: [PATCH 206/235] Add docs --- docs/compose.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 docs/compose.md diff --git a/docs/compose.md b/docs/compose.md new file mode 100644 index 00000000..27d23fa3 --- /dev/null +++ b/docs/compose.md @@ -0,0 +1,94 @@ +# Ridgeback Docker‑Compose Overview + +This document explains the `docker-compose.yml` configuration used to run Ridgeback. It covers each service, its purpose, key environment variables, and how the components interact. + +--- + +## Service Overview +| Service | Purpose | +|---------|---------| +| `ridgeback_create_volumes` | Create host directories (postgres, logs, celery, rabbitmq, server, logrotate) with the correct UID/GID. | +| `ridgeback_postgres` | PostgreSQL database instance for Ridgeback. | +| `ridgeback_memcached` | Memcached cache server. | +| `ridgeback_rabbitmq` | RabbitMQ message broker with management UI. | +| `ridgeback` | Django application that hosts the Ridgeback web interface and API. | +| `ridgeback_celery_beat` | Celery beat scheduler that triggers periodic tasks. | +| `ridgeback_celery_*_queue` | Various Celery workers that process different task queues (command, action, status, submit‑job, set‑permission, cleanup). | +| `ridgeback_logrotate` | Periodically rotates application logs. | +| `ridgeback_db_backup` | Schedules regular database backups. | + +--- + +## Detailed Service Configuration +### 1. `ridgeback_create_volumes` +* **Image**: Alpine 3.8 (minimal base). +* **Entrypoint**: Bash script that `chown -R ${DOCKER_UID}:${DOCKER_GID}` for each host directory. +* **Volumes**: Maps the following directories into the container: + * `./postgres:/postgres` + * `./logs:/logs` + * `./celery:/celery` + * `./rabbitmq:/rabbitmq` + * `./server/:/server` + * `./logrotate:/logrotate` + * `${DB_BACKUP_PATH}:/db_backup` + +### 2. Database & Cache Services +| Service | Image | Key Environment Variables | Notes | +|---------|-------|--------------------------|-------| +| `ridgeback_postgres` | `postgres:17` | `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` | Exposes port `${RIDGEBACK_DB_PORT}` (default 5432). Healthcheck via `pg_isready`. | +| `ridgeback_memcached` | `bitnami/memcached:1.6.37` | N/A | Exposes port 11211; healthcheck via `nc`. | +| `ridgeback_rabbitmq` | `rabbitmq:4.0.6-management-alpine` | `RABBITMQ_DEFAULT_USER`, `RABBITMQ_DEFAULT_PASS` | Management UI on `${RIDGEBACK_RABBITMQ_MANAGEMENT_PORT}` (default 15672). Healthcheck via `rabbitmq-diagnostics`. | + +All three depend on the volume‑creation service. + +### 3. Core Django Application (`ridgeback`) +* **Image**: `mskcc/ridgeback:${RIDGEBACK_VERSION}` +* **Environment**: Uses the same DB, cache, and RabbitMQ URLs as the workers. +* **Volumes**: + * `./logs/:/ridgeback/server/` + * `./server/:/ridgeback_staticfiles/` +* **Command**: Runs migrations, creates a superuser if none exists, collects static files, then starts the Django development server on `${RIDGEBACK_PORT}`. +* **Healthcheck**: Simple `curl` to the web endpoint. +* **Dependencies**: PostgreSQL, Memcached, RabbitMQ. + +### 4. Celery Workers & Beat +All workers share the anchor `x-ridgeback_celery` for common settings (image, user, network, env_file, volumes). Each worker overrides the `command` to start its specific queue. + +| Worker | Queue | Concurrency | Command | +|--------|-------|-------------|---------| +| `ridgeback_celery_beat` | N/A (beat) | 1 | Starts Celery beat with schedule file. | +| `ridgeback_celery_command_queue` | `${RIDGEBACK_COMMAND_QUEUE}` | 30 | Worker for command queue. | +| `ridgeback_celery_action_queue` | `${RIDGEBACK_ACTION_QUEUE}` | 10 | Worker for action queue. | +| `ridgeback_celery_check_status_queue` | `${RIDGEBACK_CHECK_STATUS_QUEUE}` | 10 | Worker for status checks. | +| `ridgeback_celery_submit_job_queue` | `${RIDGEBACK_SUBMIT_JOB_QUEUE}` | 5 | Worker for job submission. | +| `ridgeback_celery_set_permission_queue` | `${RIDGEBACK_SET_PERMISSIONS_QUEUE}` | 10 | Worker for permission setting. | +| `ridgeback_celery_cleanup_queue` | `${RIDGEBACK_CLEANUP_QUEUE}` | 2 | Worker for cleanup tasks. | + +All depend on PostgreSQL, Memcached, RabbitMQ, and `ridgeback_celery_beat` for health. + +### 5. Auxiliary Services +| Service | Image | Purpose | +|---------|-------|----------| +| `ridgeback_logrotate` | `mskcc/voyager-compose-utils:1.0.0` | Rotates logs weekly; uses supercronic to schedule logrotate runs. | +| `ridgeback_db_backup` | Same image | Schedules database backups via supercronic; uses `pg_dump`. | + +Both depend on the beat and command‑queue workers. + +### 6. Network +* **`voyager_net`** – Bridge network shared by all services. + +--- + +## Key Environment Variables +| Variable | Description | +|----------|-------------| +| `RIDGEBACK_VERSION` | Docker image tag for Ridgeback. | +| `DOCKER_UID`, `DOCKER_GID` | UID/GID for container processes. | +| `RIDGEBACK_DB_USERNAME`, `_PASSWORD`, `_NAME` | PostgreSQL credentials. | +| `RIDGEBACK_RABBITMQ_USERNAME`, `_PASSWORD` | RabbitMQ credentials. | +| `CLUSTER_FILESYSTEM_MOUNT`, `CLUSTER_SCRATCH_MOUNT`, `CLUSTER_ADMIN_MOUNT` | Bind mounts for cluster file system access. | +| `LOGROTATE_*`, `DB_BACKUP_*` | Log rotation and backup scheduling options. | + +--- + +This document provides a high‑level understanding of the Ridgeback Docker compose stack. For deeper configuration details, refer to the inline comments in `docker-compose.yml` and the respective service Dockerfiles. From 79956e8b65fadfa18ba19fce3d4637fc0559f230 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 14:00:38 -0500 Subject: [PATCH 207/235] Ignore .env files for compose --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 67398010..cd8cb177 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ conda/ toil .toil/ *.sif +.env From 5b5a3cc7dea396cd4ad676782f43fe8add49d5f6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 14:55:39 -0500 Subject: [PATCH 208/235] Updated webserver mount paths --- compose.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compose.yaml b/compose.yaml index 3aeb14a5..ac2f19be 100644 --- a/compose.yaml +++ b/compose.yaml @@ -270,6 +270,11 @@ services: - server_path:/ridgeback_staticfiles/ - ${CLUSTER_FILESYSTEM_MOUNT}:${CLUSTER_FILESYSTEM_MOUNT} - ${CLUSTER_CODE_PATH:-/dev/null}:${CLUSTER_CODE_PATH:-/dev/null} + - ${CLUSTER_SCRATCH_MOUNT}:${CLUSTER_SCRATCH_MOUNT} + - ${CLUSTER_PYTHON_MOUNT}:${CLUSTER_PYTHON_MOUNT} + - ${CLUSTER_PYTHON_LIBSO_MOUNT}:${CONTAINER_PYTHON_LIBSO_MOUNT} + - ${CLUSTER_PYTHON_LIB_MOUNT}:${CLUSTER_PYTHON_LIB_MOUNT} + - ${CLUSTER_PYTHON_INCLUDE_MOUNT}:${CLUSTER_PYTHON_INCLUDE_MOUNT} ports: - ${RIDGEBACK_PORT}:${RIDGEBACK_PORT} post_start: From 4fe3d6532bb9aef2e13ec0ea3626d4eb03c794c3 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 15:17:32 -0500 Subject: [PATCH 209/235] Added libffi6 package --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 764ac56e..592f59d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,9 @@ RUN apt-get update \ # Install alternative ssl library && wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ && dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb \ + # Install libffi6 for local python commands + && wget http://archive.ubuntu.com/ubuntu/pool/main/libf/libffi/libffi6_3.2.1-8_amd64.deb \ + && dpkg -i libffi6_3.2.1-8_amd64.deb \ # Clean up image && rm -rf /var/lib/apt/lists/* From 83941a8abaeec2d79b2ab7daae60304a3579fc2f Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:14:55 -0500 Subject: [PATCH 210/235] Store env in a tempfile --- submitter/userswitcher.py | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 3ac2c2bd..23c1e821 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -10,7 +10,7 @@ from getpass import getuser import django import json -import zlib +import tempfile from django.conf import settings log = logging.getLogger(__name__) @@ -27,9 +27,9 @@ def userscript(): output = None with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): try: - env_str_encode = sys.argv[1] - env_str = zlib.decompress(env_str_encode).decode() - env_json = json.loads(env_str) + env_path = sys.argv[1] + with open(env_path, "r") as env_file: + env_json = json.loads(env_file) for single_env in env_json: if single_env == "PATH": os.environ[single_env] = env_json[single_env] @@ -63,19 +63,20 @@ def dzdo_wrapper(*args, **kwargs): proc_command = ["dzdo", "--login", "-u", f"{user}", sys.executable, Path(__file__).absolute()] try: job_func = dill.dumps((func, args, kwargs)) - env_str = json.dumps(current_env) - env_str_encode = zlib.compress(env_str.encode()) - dzdo_process = subprocess.run( - proc_command + [env_str_encode], input=job_func, check=True, capture_output=True, env=current_env - ) - output, stdout = dill.loads(dzdo_process.stdout) - func_stdout = stdout.decode().strip() - func_stderr = dzdo_process.stderr.decode().strip() - if func_stdout: - log.info(func_stdout) - if func_stderr: - log.error(func_stderr) - return output + with tempfile.NamedTemporaryFile(mode="w+") as tmp_env_file: + os.chmod(tmp_env_file.name, 0o755) + json.dump(current_env, tmp_env_file) + dzdo_process = subprocess.run( + proc_command + [tmp_env_file.name], input=job_func, check=True, capture_output=True, env=current_env + ) + output, stdout = dill.loads(dzdo_process.stdout) + func_stdout = stdout.decode().strip() + func_stderr = dzdo_process.stderr.decode().strip() + if func_stdout: + log.info(func_stdout) + if func_stderr: + log.error(func_stderr) + return output except subprocess.CalledProcessError as e: stdout_str = "" stderr_str = "" From 09ddb6f4a1bdbf3b68ba7042f010f0e7a2a1bc6b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:25:48 -0500 Subject: [PATCH 211/235] Set env file in the tmp folder in the container --- submitter/userswitcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 23c1e821..7ab5a077 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -63,7 +63,7 @@ def dzdo_wrapper(*args, **kwargs): proc_command = ["dzdo", "--login", "-u", f"{user}", sys.executable, Path(__file__).absolute()] try: job_func = dill.dumps((func, args, kwargs)) - with tempfile.NamedTemporaryFile(mode="w+") as tmp_env_file: + with tempfile.NamedTemporaryFile(mode="w+", dir="/tmp") as tmp_env_file: os.chmod(tmp_env_file.name, 0o755) json.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( From 10e8759d15a74f2eca5c8ddcf44ecdc922dd3728 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:28:01 -0500 Subject: [PATCH 212/235] Fixed typo on json loading --- submitter/userswitcher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 7ab5a077..94e1d94c 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -29,7 +29,7 @@ def userscript(): try: env_path = sys.argv[1] with open(env_path, "r") as env_file: - env_json = json.loads(env_file) + env_json = json.load(env_file) for single_env in env_json: if single_env == "PATH": os.environ[single_env] = env_json[single_env] From 1cf4b9b24848507d17354efb9b1a938ed96a4536 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:33:56 -0500 Subject: [PATCH 213/235] Add strict file encodings --- submitter/userswitcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 94e1d94c..c194b044 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -28,7 +28,7 @@ def userscript(): with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): try: env_path = sys.argv[1] - with open(env_path, "r") as env_file: + with open(env_path, "r", encoding="utf8") as env_file: env_json = json.load(env_file) for single_env in env_json: if single_env == "PATH": @@ -63,7 +63,7 @@ def dzdo_wrapper(*args, **kwargs): proc_command = ["dzdo", "--login", "-u", f"{user}", sys.executable, Path(__file__).absolute()] try: job_func = dill.dumps((func, args, kwargs)) - with tempfile.NamedTemporaryFile(mode="w+", dir="/tmp") as tmp_env_file: + with tempfile.NamedTemporaryFile(mode="w+", dir="/tmp", encoding="utf8") as tmp_env_file: os.chmod(tmp_env_file.name, 0o755) json.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( From 28f7e7c5f0bf0271e36b4dcb01c5f02322dbd5ed Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:40:57 -0500 Subject: [PATCH 214/235] Dill dump env to a file --- submitter/userswitcher.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index c194b044..920cc43f 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -9,7 +9,6 @@ from functools import wraps from getpass import getuser import django -import json import tempfile from django.conf import settings @@ -29,12 +28,12 @@ def userscript(): try: env_path = sys.argv[1] with open(env_path, "r", encoding="utf8") as env_file: - env_json = json.load(env_file) - for single_env in env_json: + env_dict = dill.load(env_file) + for single_env in env_dict: if single_env == "PATH": - os.environ[single_env] = env_json[single_env] + os.environ[single_env] = env_dict[single_env] else: - os.environ.setdefault(single_env, env_json[single_env]) + os.environ.setdefault(single_env, env_dict[single_env]) django.setup() func_data = sys.stdin.buffer.read() func, args, kwargs = dill.loads(func_data) @@ -65,7 +64,7 @@ def dzdo_wrapper(*args, **kwargs): job_func = dill.dumps((func, args, kwargs)) with tempfile.NamedTemporaryFile(mode="w+", dir="/tmp", encoding="utf8") as tmp_env_file: os.chmod(tmp_env_file.name, 0o755) - json.dump(current_env, tmp_env_file) + dill.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( proc_command + [tmp_env_file.name], input=job_func, check=True, capture_output=True, env=current_env ) From 39783ba7c2a963c1259c58d31b4163c097ef18da Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:44:38 -0500 Subject: [PATCH 215/235] Set file modes to binary mode --- submitter/userswitcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 920cc43f..2a8284d8 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -27,7 +27,7 @@ def userscript(): with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): try: env_path = sys.argv[1] - with open(env_path, "r", encoding="utf8") as env_file: + with open(env_path, "rb", encoding="utf8") as env_file: env_dict = dill.load(env_file) for single_env in env_dict: if single_env == "PATH": @@ -62,7 +62,7 @@ def dzdo_wrapper(*args, **kwargs): proc_command = ["dzdo", "--login", "-u", f"{user}", sys.executable, Path(__file__).absolute()] try: job_func = dill.dumps((func, args, kwargs)) - with tempfile.NamedTemporaryFile(mode="w+", dir="/tmp", encoding="utf8") as tmp_env_file: + with tempfile.NamedTemporaryFile(mode="wb", dir="/tmp", encoding="utf8") as tmp_env_file: os.chmod(tmp_env_file.name, 0o755) dill.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( From 2e84a2761b06f9ff625dca3e9a7c706959ac7f2e Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:45:55 -0500 Subject: [PATCH 216/235] More file fixes --- submitter/userswitcher.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 2a8284d8..8d6fcbde 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -27,7 +27,7 @@ def userscript(): with contextlib.redirect_stdout(stdout_buffer), contextlib.redirect_stderr(stderr_buffer): try: env_path = sys.argv[1] - with open(env_path, "rb", encoding="utf8") as env_file: + with open(env_path, "rb") as env_file: env_dict = dill.load(env_file) for single_env in env_dict: if single_env == "PATH": @@ -62,7 +62,7 @@ def dzdo_wrapper(*args, **kwargs): proc_command = ["dzdo", "--login", "-u", f"{user}", sys.executable, Path(__file__).absolute()] try: job_func = dill.dumps((func, args, kwargs)) - with tempfile.NamedTemporaryFile(mode="wb", dir="/tmp", encoding="utf8") as tmp_env_file: + with tempfile.NamedTemporaryFile(mode="wb", dir="/tmp") as tmp_env_file: os.chmod(tmp_env_file.name, 0o755) dill.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( From 3f2f90e72401a401aacec3a2c7ddcaf51fdb147d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 16:57:51 -0500 Subject: [PATCH 217/235] Fixed userscript function --- submitter/userswitcher.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 8d6fcbde..bf355bd7 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -16,10 +16,6 @@ def userscript(): - - ridgeback_path = os.environ.get("RIDGEBACK_PATH") - sys.path.append(ridgeback_path) - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ridgeback.settings") stdout_buffer = io.StringIO() stderr_buffer = io.StringIO() exception_raised = False @@ -34,6 +30,9 @@ def userscript(): os.environ[single_env] = env_dict[single_env] else: os.environ.setdefault(single_env, env_dict[single_env]) + ridgeback_path = env_dict["RIDGEBACK_PATH"] + sys.path.append(ridgeback_path) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ridgeback.settings") django.setup() func_data = sys.stdin.buffer.read() func, args, kwargs = dill.loads(func_data) From 9a8ab45603101662d809b5a0c882effb8bb8e86b Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 3 Nov 2025 18:15:12 -0500 Subject: [PATCH 218/235] Added fix for healthchecks run in sh --- compose.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compose.yaml b/compose.yaml index ac2f19be..ff0373c3 100644 --- a/compose.yaml +++ b/compose.yaml @@ -107,7 +107,7 @@ x-ridgeback_celery: &ridgeback_celery read_only: true entrypoint: ["/bin/bash", "-c"] healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status || exit 1" interval: 30s timeout: 3s retries: 3 @@ -387,7 +387,7 @@ services: --concurrency=30 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} || exit 1" interval: 30s timeout: 3s retries: 3 @@ -411,7 +411,7 @@ services: --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} || exit 1" interval: 30s timeout: 3s retries: 3 @@ -434,7 +434,7 @@ services: --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} || exit 1" interval: 30s timeout: 3s retries: 3 @@ -457,7 +457,7 @@ services: --concurrency=5 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} || exit 1" interval: 30s timeout: 3s retries: 3 @@ -480,7 +480,7 @@ services: --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} || exit 1" interval: 30s timeout: 3s retries: 3 @@ -503,7 +503,7 @@ services: --concurrency=2 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} healthcheck: - test: "source ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} || exit 1" interval: 30s timeout: 3s retries: 3 From e09bc32f0906ad2581fe9e533b869826c210b925 Mon Sep 17 00:00:00 2001 From: buehlere Date: Thu, 13 Nov 2025 17:26:59 -0500 Subject: [PATCH 219/235] updating XS submissions to use newer toil version --- ridgeback/settings.py | 2 +- submitter/toil_submitter/toil_jobsubmitter.py | 59 +------------------ 2 files changed, 3 insertions(+), 58 deletions(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index ec7fa27e..39caec2e 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -328,7 +328,7 @@ # ACCESS LEGACY INFO ACCESS_LEGACY_APP = os.environ.get("ACCESS_LEGACY_APP", "access-pipeline") ACCESS_LEGACY_CONDA_ENV = os.environ.get( - "ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS/bin" + "ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS-voyager/bin" ) # SHELL_PLUS = "ipython" diff --git a/submitter/toil_submitter/toil_jobsubmitter.py b/submitter/toil_submitter/toil_jobsubmitter.py index 5e933b76..bffa2ace 100644 --- a/submitter/toil_submitter/toil_jobsubmitter.py +++ b/submitter/toil_submitter/toil_jobsubmitter.py @@ -90,10 +90,7 @@ def get_submit_command(self): env["TMPDIR"] = self.job_tmp_dir env[self.batch_system_args_env] = toil_batch_system_args.strip() if settings.ACCESS_LEGACY_APP in self.app.github.lower(): - env["PATH"] = "{0}:{1}".format(settings.ACCESS_LEGACY_CONDA_ENV, os.environ.get("PATH")) - env[self.batch_system_args_env] = " ".join( - [env[self.batch_system_args_env], self.batch_system.get_env_export_flag()] - ) + env["PATH"] = f"{settings.ACCESS_LEGACY_CONDA_ENV}:{os.environ.get('PATH')}" return command_line, self._leader_args(), log_path, self.job_id, self.partition, env def get_commandline_status(self, cache): @@ -233,59 +230,7 @@ def _job_group(self): def _command_line(self): single_machine = any([w in self.app.github.lower() for w in self.single_machine_mode_workflows]) - if settings.ACCESS_LEGACY_APP in self.app.github.lower(): - """ - Start ACCESS-specific code - """ - command_line = [ - "toil-cwl-runner", - "--logLevel=INFO", - "--no-container", - "--logFile", - "toil_log.log", - "--batchSystem", - self.batch_system.name, - "--disable-user-provenance", - "--disable-host-provenance", - "--cleanWorkDir", - "onSuccess", - "--disableCaching", - "--preserve-environment", - "PATH", - "TMPDIR", - "PWD", - "_JAVA_OPTIONS", - "PYTHONPATH", - "TEMP", - self.batch_system_args_env, - "PWD", - "TOIL_SLURM_ARGS", - "--disableChaining", - "--maxCores", - "24", - "--maxMemory", - "256G", - "--defaultMemory", - "10G", - "--defaultDisk", - "20G", - "--not-strict", - "--runCwlInternalJobsOnWorkers", - "--jobStore", - self.job_store_dir, - "--tmpdir-prefix", - self.job_tmp_dir, - "--workDir", - self.job_work_dir, - "--outdir", - self.job_outputs_dir, - "--maxLocalJobs", - "500", - ] - """ - End ACCESS-specific code - """ - elif single_machine: + if single_machine: command_line = [ settings.CWLTOIL, "--singularity", From 306c47546781680685203b859d22295c793f68de Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 17 Nov 2025 16:23:07 -0500 Subject: [PATCH 220/235] Updated README doc on docker compose --- docs/compose.md | 118 +++++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 71 deletions(-) diff --git a/docs/compose.md b/docs/compose.md index 27d23fa3..9420d114 100644 --- a/docs/compose.md +++ b/docs/compose.md @@ -1,94 +1,70 @@ # Ridgeback Docker‑Compose Overview -This document explains the `docker-compose.yml` configuration used to run Ridgeback. It covers each service, its purpose, key environment variables, and how the components interact. - ---- +This document explains the `compose.yml` configuration used to run Ridgeback. It covers each service, its purpose, key environment variables, and how the components interact. ## Service Overview -| Service | Purpose | -|---------|---------| -| `ridgeback_create_volumes` | Create host directories (postgres, logs, celery, rabbitmq, server, logrotate) with the correct UID/GID. | -| `ridgeback_postgres` | PostgreSQL database instance for Ridgeback. | -| `ridgeback_memcached` | Memcached cache server. | -| `ridgeback_rabbitmq` | RabbitMQ message broker with management UI. | -| `ridgeback` | Django application that hosts the Ridgeback web interface and API. | -| `ridgeback_celery_beat` | Celery beat scheduler that triggers periodic tasks. | + +| Service | Purpose | +| -------------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `ridgeback_create_volumes` | Create host directories (postgres, logs, celery, rabbitmq, server, logrotate) with the correct UID/GID. | +| `ridgeback_postgres` | PostgreSQL database instance for Ridgeback. | +| `ridgeback_memcached` | Memcached cache server. | +| `ridgeback_rabbitmq` | RabbitMQ message broker with management UI. | +| `ridgeback` | Django application that hosts the Ridgeback web interface and API. | +| `ridgeback_celery_beat` | Celery beat scheduler that triggers periodic tasks. | | `ridgeback_celery_*_queue` | Various Celery workers that process different task queues (command, action, status, submit‑job, set‑permission, cleanup). | -| `ridgeback_logrotate` | Periodically rotates application logs. | -| `ridgeback_db_backup` | Schedules regular database backups. | +| `ridgeback_logrotate` | Periodically rotates application logs. | +| `ridgeback_db_backup` | Schedules regular database backups. | --- -## Detailed Service Configuration -### 1. `ridgeback_create_volumes` -* **Image**: Alpine 3.8 (minimal base). -* **Entrypoint**: Bash script that `chown -R ${DOCKER_UID}:${DOCKER_GID}` for each host directory. -* **Volumes**: Maps the following directories into the container: - * `./postgres:/postgres` - * `./logs:/logs` - * `./celery:/celery` - * `./rabbitmq:/rabbitmq` - * `./server/:/server` - * `./logrotate:/logrotate` - * `${DB_BACKUP_PATH}:/db_backup` - -### 2. Database & Cache Services -| Service | Image | Key Environment Variables | Notes | -|---------|-------|--------------------------|-------| -| `ridgeback_postgres` | `postgres:17` | `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` | Exposes port `${RIDGEBACK_DB_PORT}` (default 5432). Healthcheck via `pg_isready`. | -| `ridgeback_memcached` | `bitnami/memcached:1.6.37` | N/A | Exposes port 11211; healthcheck via `nc`. | -| `ridgeback_rabbitmq` | `rabbitmq:4.0.6-management-alpine` | `RABBITMQ_DEFAULT_USER`, `RABBITMQ_DEFAULT_PASS` | Management UI on `${RIDGEBACK_RABBITMQ_MANAGEMENT_PORT}` (default 15672). Healthcheck via `rabbitmq-diagnostics`. | +### 1. Database & Cache Services + +| Service | Image | Key Environment Variables | Notes | +| --------------------- | ---------------------------------- | --------------------------------------------------- | -------------------------------------------------------- | +| `ridgeback_postgres` | `postgres:17` | `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB` | Exposes port `${RIDGEBACK_DB_PORT}` | +| `ridgeback_memcached` | `mskcc/memcached-nc:1.6.39` | `RIDGEBACK_MEMCACHED_PORT` | Exposes port `${RIDGEBACK_MEMCACHED_PORT}` | +| `ridgeback_rabbitmq` | `rabbitmq:4.0.6-management-alpine` | `RABBITMQ_DEFAULT_USER`, `RABBITMQ_DEFAULT_PASS` | Management UI on `${RIDGEBACK_RABBITMQ_MANAGEMENT_PORT}` | All three depend on the volume‑creation service. -### 3. Core Django Application (`ridgeback`) -* **Image**: `mskcc/ridgeback:${RIDGEBACK_VERSION}` -* **Environment**: Uses the same DB, cache, and RabbitMQ URLs as the workers. -* **Volumes**: - * `./logs/:/ridgeback/server/` - * `./server/:/ridgeback_staticfiles/` -* **Command**: Runs migrations, creates a superuser if none exists, collects static files, then starts the Django development server on `${RIDGEBACK_PORT}`. -* **Healthcheck**: Simple `curl` to the web endpoint. -* **Dependencies**: PostgreSQL, Memcached, RabbitMQ. - -### 4. Celery Workers & Beat -All workers share the anchor `x-ridgeback_celery` for common settings (image, user, network, env_file, volumes). Each worker overrides the `command` to start its specific queue. - -| Worker | Queue | Concurrency | Command | -|--------|-------|-------------|---------| -| `ridgeback_celery_beat` | N/A (beat) | 1 | Starts Celery beat with schedule file. | -| `ridgeback_celery_command_queue` | `${RIDGEBACK_COMMAND_QUEUE}` | 30 | Worker for command queue. | -| `ridgeback_celery_action_queue` | `${RIDGEBACK_ACTION_QUEUE}` | 10 | Worker for action queue. | -| `ridgeback_celery_check_status_queue` | `${RIDGEBACK_CHECK_STATUS_QUEUE}` | 10 | Worker for status checks. | -| `ridgeback_celery_submit_job_queue` | `${RIDGEBACK_SUBMIT_JOB_QUEUE}` | 5 | Worker for job submission. | -| `ridgeback_celery_set_permission_queue` | `${RIDGEBACK_SET_PERMISSIONS_QUEUE}` | 10 | Worker for permission setting. | -| `ridgeback_celery_cleanup_queue` | `${RIDGEBACK_CLEANUP_QUEUE}` | 2 | Worker for cleanup tasks. | +### 2. Celery Workers & Beat + +All workers share the anchor `x-ridgeback_celery` for common settings (image, user, network, env_file, volumes). Each worker overrides the `command` to start its specific queue. + +| Worker | Queue | Concurrency | Command | +| --------------------------------------- | ------------------------------------ | ----------- | -------------------------------------- | +| `ridgeback_celery_beat` | N/A (beat) | 1 | Starts Celery beat with schedule file. | +| `ridgeback_celery_command_queue` | `${RIDGEBACK_COMMAND_QUEUE}` | 30 | Worker for command queue. | +| `ridgeback_celery_action_queue` | `${RIDGEBACK_ACTION_QUEUE}` | 10 | Worker for action queue. | +| `ridgeback_celery_check_status_queue` | `${RIDGEBACK_CHECK_STATUS_QUEUE}` | 10 | Worker for status checks. | +| `ridgeback_celery_submit_job_queue` | `${RIDGEBACK_SUBMIT_JOB_QUEUE}` | 5 | Worker for job submission. | +| `ridgeback_celery_set_permission_queue` | `${RIDGEBACK_SET_PERMISSIONS_QUEUE}` | 10 | Worker for permission setting. | +| `ridgeback_celery_cleanup_queue` | `${RIDGEBACK_CLEANUP_QUEUE}` | 2 | Worker for cleanup tasks. | All depend on PostgreSQL, Memcached, RabbitMQ, and `ridgeback_celery_beat` for health. ### 5. Auxiliary Services -| Service | Image | Purpose | -|---------|-------|----------| -| `ridgeback_logrotate` | `mskcc/voyager-compose-utils:1.0.0` | Rotates logs weekly; uses supercronic to schedule logrotate runs. | -| `ridgeback_db_backup` | Same image | Schedules database backups via supercronic; uses `pg_dump`. | + +| Service | Image | Purpose | +| --------------------- | ----------------------------------- | ------------------------------------------- | +| `ridgeback_logrotate` | `mskcc/voyager-compose-utils:1.0.0` | Rotates logs weekly | +| `ridgeback_db_backup` | Same image | Schedules database backups using `pg_dump`. | Both depend on the beat and command‑queue workers. ### 6. Network -* **`voyager_net`** – Bridge network shared by all services. ---- +- **`voyager_net`** – Bridge network shared by all services and also [Beagle](https://github.com/mskcc/beagle) ## Key Environment Variables -| Variable | Description | -|----------|-------------| -| `RIDGEBACK_VERSION` | Docker image tag for Ridgeback. | -| `DOCKER_UID`, `DOCKER_GID` | UID/GID for container processes. | -| `RIDGEBACK_DB_USERNAME`, `_PASSWORD`, `_NAME` | PostgreSQL credentials. | -| `RIDGEBACK_RABBITMQ_USERNAME`, `_PASSWORD` | RabbitMQ credentials. | -| `CLUSTER_FILESYSTEM_MOUNT`, `CLUSTER_SCRATCH_MOUNT`, `CLUSTER_ADMIN_MOUNT` | Bind mounts for cluster file system access. | -| `LOGROTATE_*`, `DB_BACKUP_*` | Log rotation and backup scheduling options. | - ---- -This document provides a high‑level understanding of the Ridgeback Docker compose stack. For deeper configuration details, refer to the inline comments in `docker-compose.yml` and the respective service Dockerfiles. +| Variable | Description | +| -------------------------------------------------------------------------- | ----------------------------------------------- | +| `RIDGEBACK_VERSION` | Docker image tag for Ridgeback. | +| `DOCKER_UID`, `DOCKER_GID` | UID/GID for container processes. | +| `RIDGEBACK_DB_USERNAME`, `_PASSWORD`, `_NAME` | PostgreSQL credentials. | +| `RIDGEBACK_RABBITMQ_USERNAME`, `_PASSWORD` | RabbitMQ credentials. | +| `CLUSTER_FILESYSTEM_MOUNT`, `CLUSTER_SCRATCH_MOUNT`, `CLUSTER_ADMIN_MOUNT` | Bind mounts for cluster file system access. | +| `CLUSTER_CODE_PATH` | Path for the ridgeback code base on the cluster | +| `LOGROTATE_*`, `DB_BACKUP_*` | Log rotation and backup scheduling options. | From 732815aebcdc24e385305a173aa1bbf40c6e912a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 17 Nov 2025 16:29:36 -0500 Subject: [PATCH 221/235] Update ordering of compose.md --- docs/compose.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/compose.md b/docs/compose.md index 9420d114..f16692f7 100644 --- a/docs/compose.md +++ b/docs/compose.md @@ -44,7 +44,7 @@ All workers share the anchor `x-ridgeback_celery` for common settings (image, us All depend on PostgreSQL, Memcached, RabbitMQ, and `ridgeback_celery_beat` for health. -### 5. Auxiliary Services +### 3. Auxiliary Services | Service | Image | Purpose | | --------------------- | ----------------------------------- | ------------------------------------------- | @@ -53,7 +53,7 @@ All depend on PostgreSQL, Memcached, RabbitMQ, and `ridgeback_celery_beat` for h Both depend on the beat and command‑queue workers. -### 6. Network +### 4. Network - **`voyager_net`** – Bridge network shared by all services and also [Beagle](https://github.com/mskcc/beagle) From 7489aa40d0747b28f6cc8c246c6b0c9bc52818fa Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 18 Nov 2025 13:54:17 -0500 Subject: [PATCH 222/235] Update compose.md --- docs/compose.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compose.md b/docs/compose.md index f16692f7..6ff29be5 100644 --- a/docs/compose.md +++ b/docs/compose.md @@ -1,4 +1,4 @@ -# Ridgeback Docker‑Compose Overview +# Ridgeback Docker Compose Overview This document explains the `compose.yml` configuration used to run Ridgeback. It covers each service, its purpose, key environment variables, and how the components interact. From bb0f7b8f8c6c27d4ff4a29ba5db5eedf81f05a3a Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 3 Dec 2025 14:37:27 -0500 Subject: [PATCH 223/235] Collection of fixes to handle ridgeback zombie processes --- submitter/userswitcher.py | 50 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index bf355bd7..7b3dd333 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -10,11 +10,31 @@ from getpass import getuser import django import tempfile +import psutil +import signal +import atexit + from django.conf import settings log = logging.getLogger(__name__) +def kill_proc_tree(): + current_pid = os.getpid() + sig = signal.SIGTERM + main = psutil.Process(current_pid) + children = main.children(recursive=True) + for child in children: + try: + child.send_signal(sig) + except psutil.NoSuchProcess: + pass + psutil.wait_procs(children, timeout=5) + + +atexit.register(kill_proc_tree) + + def userscript(): stdout_buffer = io.StringIO() stderr_buffer = io.StringIO() @@ -65,7 +85,13 @@ def dzdo_wrapper(*args, **kwargs): os.chmod(tmp_env_file.name, 0o755) dill.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( - proc_command + [tmp_env_file.name], input=job_func, check=True, capture_output=True, env=current_env + proc_command + [tmp_env_file.name], + input=job_func, + check=True, + capture_output=True, + env=current_env, + start_new_session=True, + timeout=43200, ) output, stdout = dill.loads(dzdo_process.stdout) func_stdout = stdout.decode().strip() @@ -75,6 +101,26 @@ def dzdo_wrapper(*args, **kwargs): if func_stderr: log.error(func_stderr) return output + except subprocess.TimeoutExpired as timout_err: + stdout_str = "" + stderr_str = "" + try: + stderr = timout_err.stderr + if stderr: + stderr_str = stderr.decode().strip() + output, stdout = dill.loads(timout_err.output) + if stdout: + stdout_str = stdout.decode().strip() + except Exception: + stdout_str = "NA" + exception_message = f""" + Timeout error while userswitching: + USER: {user} + Timeout after {timout_err.timeout} seconds. + Output: {stdout_str} + Error: {stderr_str} + """ + raise Exception(exception_message) except subprocess.CalledProcessError as e: stdout_str = "" stderr_str = "" @@ -101,6 +147,8 @@ def dzdo_wrapper(*args, **kwargs): {e.filename} not found. """ raise Exception(exception_message) + finally: + kill_proc_tree() return dzdo_wrapper From 97fb60632cdcf524afb23a36a0dd9023d100b6c8 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 4 Dec 2025 13:52:55 -0500 Subject: [PATCH 224/235] Update timeout to prevent excessive restarts --- compose.yaml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compose.yaml b/compose.yaml index ff0373c3..8852f04b 100644 --- a/compose.yaml +++ b/compose.yaml @@ -109,7 +109,7 @@ x-ridgeback_celery: &ridgeback_celery healthcheck: test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status || exit 1" interval: 30s - timeout: 3s + timeout: 10s retries: 3 depends_on: ridgeback_postgres: @@ -204,7 +204,7 @@ services: "sh -c 'pg_isready -U ${RIDGEBACK_DB_USERNAME} -d ${RIDGEBACK_DB_NAME}'", ] interval: 30s - timeout: 3s + timeout: 10s retries: 3 depends_on: - ridgeback_create_volumes @@ -220,7 +220,7 @@ services: healthcheck: test: ["CMD", "nc", "-z", "localhost", "11211"] interval: 30s - timeout: 5s + timeout: 10s retries: 3 ridgeback_rabbitmq: image: rabbitmq:4.0.6-management-alpine @@ -243,7 +243,7 @@ services: healthcheck: test: ["CMD", "rabbitmq-diagnostics", "check_running"] interval: 30s - timeout: 3s + timeout: 10s retries: 3 depends_on: - ridgeback_create_volumes @@ -315,7 +315,7 @@ services: test: ["CMD-SHELL", "curl -sSf http://localhost:${RIDGEBACK_PORT}/ || exit 1"] interval: 30s - timeout: 3s + timeout: 10s retries: 3 depends_on: ridgeback_postgres: @@ -387,9 +387,9 @@ services: --concurrency=30 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} healthcheck: - test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} || exit 1" + test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE}" interval: 30s - timeout: 3s + timeout: 10s retries: 3 ridgeback_celery_action_queue: <<: *ridgeback_celery @@ -413,7 +413,7 @@ services: healthcheck: test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} || exit 1" interval: 30s - timeout: 3s + timeout: 10s retries: 3 ridgeback_celery_check_status_queue: <<: *ridgeback_celery @@ -436,7 +436,7 @@ services: healthcheck: test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} || exit 1" interval: 30s - timeout: 3s + timeout: 10s retries: 3 ridgeback_celery_submit_job_queue: <<: *ridgeback_celery @@ -459,7 +459,7 @@ services: healthcheck: test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} || exit 1" interval: 30s - timeout: 3s + timeout: 10s retries: 3 ridgeback_celery_set_permission_queue: <<: *ridgeback_celery @@ -482,7 +482,7 @@ services: healthcheck: test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} || exit 1" interval: 30s - timeout: 3s + timeout: 10s retries: 3 ridgeback_celery_cleanup_queue: <<: *ridgeback_celery @@ -505,7 +505,7 @@ services: healthcheck: test: ". ${RIDGEBACK_VENV}/bin/activate; celery --workdir ${RIDGEBACK_PATH} -A orchestrator status -d celery@ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} || exit 1" interval: 30s - timeout: 3s + timeout: 10s retries: 3 ridgeback_logrotate: image: mskcc/voyager-compose-utils:1.0.1 @@ -539,7 +539,7 @@ services: healthcheck: test: "find /logs/last_completed_logrotate_cron -type f -mtime -2 | read" interval: 12h - timeout: 5h + timeout: 10s retries: 3 depends_on: ridgeback_celery_beat: @@ -570,7 +570,7 @@ services: healthcheck: test: "find /logs/last_completed_db_backup_cron -type f -mtime -2 | read" interval: 12h - timeout: 5h + timeout: 10s retries: 3 depends_on: ridgeback_celery_beat: From b49040f3511f5d636f3701ee0b5b09b8dcf4cfed Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Thu, 4 Dec 2025 13:53:23 -0500 Subject: [PATCH 225/235] Revert "Collection of fixes to handle ridgeback zombie processes" This reverts commit bb0f7b8f8c6c27d4ff4a29ba5db5eedf81f05a3a. --- submitter/userswitcher.py | 50 +-------------------------------------- 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/submitter/userswitcher.py b/submitter/userswitcher.py index 7b3dd333..bf355bd7 100755 --- a/submitter/userswitcher.py +++ b/submitter/userswitcher.py @@ -10,31 +10,11 @@ from getpass import getuser import django import tempfile -import psutil -import signal -import atexit - from django.conf import settings log = logging.getLogger(__name__) -def kill_proc_tree(): - current_pid = os.getpid() - sig = signal.SIGTERM - main = psutil.Process(current_pid) - children = main.children(recursive=True) - for child in children: - try: - child.send_signal(sig) - except psutil.NoSuchProcess: - pass - psutil.wait_procs(children, timeout=5) - - -atexit.register(kill_proc_tree) - - def userscript(): stdout_buffer = io.StringIO() stderr_buffer = io.StringIO() @@ -85,13 +65,7 @@ def dzdo_wrapper(*args, **kwargs): os.chmod(tmp_env_file.name, 0o755) dill.dump(current_env, tmp_env_file) dzdo_process = subprocess.run( - proc_command + [tmp_env_file.name], - input=job_func, - check=True, - capture_output=True, - env=current_env, - start_new_session=True, - timeout=43200, + proc_command + [tmp_env_file.name], input=job_func, check=True, capture_output=True, env=current_env ) output, stdout = dill.loads(dzdo_process.stdout) func_stdout = stdout.decode().strip() @@ -101,26 +75,6 @@ def dzdo_wrapper(*args, **kwargs): if func_stderr: log.error(func_stderr) return output - except subprocess.TimeoutExpired as timout_err: - stdout_str = "" - stderr_str = "" - try: - stderr = timout_err.stderr - if stderr: - stderr_str = stderr.decode().strip() - output, stdout = dill.loads(timout_err.output) - if stdout: - stdout_str = stdout.decode().strip() - except Exception: - stdout_str = "NA" - exception_message = f""" - Timeout error while userswitching: - USER: {user} - Timeout after {timout_err.timeout} seconds. - Output: {stdout_str} - Error: {stderr_str} - """ - raise Exception(exception_message) except subprocess.CalledProcessError as e: stdout_str = "" stderr_str = "" @@ -147,8 +101,6 @@ def dzdo_wrapper(*args, **kwargs): {e.filename} not found. """ raise Exception(exception_message) - finally: - kill_proc_tree() return dzdo_wrapper From 75323a94b80a11ef7e170c2be7e88baa35d6fdb6 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 8 Dec 2025 12:47:06 -0500 Subject: [PATCH 226/235] Set max tasks for periodic cleanup --- compose.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compose.yaml b/compose.yaml index 8852f04b..c2d3a0d5 100644 --- a/compose.yaml +++ b/compose.yaml @@ -384,6 +384,7 @@ services: -Q ${RIDGEBACK_COMMAND_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ --pidfile $$PIDFILE \ + --max-tasks-per-child=30 \ --concurrency=30 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} healthcheck: @@ -408,6 +409,7 @@ services: -Q ${RIDGEBACK_ACTION_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ --pidfile $$PIDFILE \ + --max-tasks-per-child=30 \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} healthcheck: @@ -431,6 +433,7 @@ services: -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ --pidfile $$PIDFILE \ + --max-tasks-per-child=30 \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} healthcheck: @@ -454,6 +457,7 @@ services: -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ --pidfile $$PIDFILE \ + --max-tasks-per-child=30 \ --concurrency=5 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} healthcheck: @@ -477,6 +481,7 @@ services: -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ --pidfile $$PIDFILE \ + --max-tasks-per-child=30 \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} healthcheck: @@ -500,6 +505,7 @@ services: -Q ${RIDGEBACK_CLEANUP_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ --pidfile $$PIDFILE \ + --max-tasks-per-child=30 \ --concurrency=2 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} healthcheck: From 2cc2b34942915237862a647d76c17f172f700915 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Mon, 8 Dec 2025 17:09:05 -0500 Subject: [PATCH 227/235] Add mounts for bot login scripts --- compose.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compose.yaml b/compose.yaml index c2d3a0d5..0112aad5 100644 --- a/compose.yaml +++ b/compose.yaml @@ -53,6 +53,14 @@ x-ridgeback_celery: &ridgeback_celery - ${CLUSTER_PYTHON_LIB_MOUNT}:${CLUSTER_PYTHON_LIB_MOUNT} - ${CLUSTER_PYTHON_INCLUDE_MOUNT}:${CLUSTER_PYTHON_INCLUDE_MOUNT} - ${CLUSTER_CODE_PATH:-/dev/null}:${CLUSTER_CODE_PATH:-/dev/null} + - type: bind + source: /etc/profile + target: /etc/profile + read_only: true + - type: bind + source: /etc/bashrc + target: /etc/bashrc + read_only: true - type: bind source: ${SLURM_ETC} target: ${SLURM_ETC} From aaa2e2d2ced6809fe33d8782c8ddc77d5d257e17 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 9 Dec 2025 14:55:52 -0500 Subject: [PATCH 228/235] Set init setting to manage zombie processes --- compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/compose.yaml b/compose.yaml index 0112aad5..0acfac2e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -2,6 +2,7 @@ name: "Ridgeback Services ${RIDGEBACK_DEPLOYMENT}" x-ridgeback_celery: &ridgeback_celery image: mskcc/ridgeback:${RIDGEBACK_VERSION} + init: true restart: always user: "${DOCKER_UID}:${DOCKER_GID}" networks: From 438ae4405335c6a90c0c3322aea6d62524995199 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 9 Dec 2025 14:56:09 -0500 Subject: [PATCH 229/235] Handle case where base_dir is not owned by user --- orchestrator/tasks.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/orchestrator/tasks.py b/orchestrator/tasks.py index 67016e12..8cc0280b 100644 --- a/orchestrator/tasks.py +++ b/orchestrator/tasks.py @@ -3,6 +3,8 @@ import shutil import logging import tempfile +from pathlib import Path +from getpass import getuser from datetime import timedelta from celery import shared_task from django.conf import settings @@ -493,7 +495,10 @@ def set_permission(job): except Exception: raise TypeError("Could not convert %s to permission octal" % str(permission_str)) try: - os.chmod(permissions_dir, permission_octal) + if Path(permissions_dir).owner() == getuser(): + os.chmod(permissions_dir, permission_octal) + else: + logger.debug(f"Skipping permission change for {permissions_dir} as it is not owned by {getuser()}") for root, dirs, files in os.walk(permissions_dir): for single_dir in dirs: if oct(os.lstat(os.path.join(root, single_dir)).st_mode)[-3:] != permission_octal: From 058846b32c493f9ddf61862ca91288edd00b7693 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Tue, 9 Dec 2025 15:27:07 -0500 Subject: [PATCH 230/235] Enable ipython --- ridgeback/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ridgeback/settings.py b/ridgeback/settings.py index 39caec2e..666e1ae5 100644 --- a/ridgeback/settings.py +++ b/ridgeback/settings.py @@ -331,4 +331,4 @@ "ACCESS_LEGACY_CONDA_ENV", "/usersoftware/core005/access/production/V1/micromamba/envs/ACCESS-voyager/bin" ) -# SHELL_PLUS = "ipython" +SHELL_PLUS = "ipython" From 3598e6220b04a92f335201cf3037ad79625c4689 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 10 Dec 2025 10:38:54 -0500 Subject: [PATCH 231/235] Increase root permission size for setting the suid/guid bit --- orchestrator/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orchestrator/models.py b/orchestrator/models.py index 0afb02a7..6f2f57c8 100755 --- a/orchestrator/models.py +++ b/orchestrator/models.py @@ -176,7 +176,7 @@ class Job(BaseModel): external_id = models.CharField(max_length=50, null=True, blank=True) base_dir = models.CharField(max_length=1000) root_dir = models.CharField(max_length=1000) - root_permission = models.CharField(default=get_default_for_job("root_permission"), max_length=3) + root_permission = models.CharField(default=get_default_for_job("root_permission"), max_length=4) user = models.CharField(default=getuser(), max_length=100) output_uid = models.IntegerField(default=get_default_for_job("output_gid"), editable=True) output_gid = models.IntegerField(default=get_default_for_job("output_gid"), editable=True) From de4139cea03443fd5380ce6a54faf09b4a331288 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Wed, 10 Dec 2025 10:43:54 -0500 Subject: [PATCH 232/235] Add migration to increase root_permission length --- .../migrations/0023_auto_20251210_1043.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 orchestrator/migrations/0023_auto_20251210_1043.py diff --git a/orchestrator/migrations/0023_auto_20251210_1043.py b/orchestrator/migrations/0023_auto_20251210_1043.py new file mode 100644 index 00000000..5dfd89c8 --- /dev/null +++ b/orchestrator/migrations/0023_auto_20251210_1043.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2025-12-10 15:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orchestrator', '0022_auto_20250805_1406'), + ] + + operations = [ + migrations.AlterField( + model_name='job', + name='root_permission', + field=models.CharField(default='750', max_length=4), + ), + ] From d28ab108d476f25d9b585b9584904010aadb598d Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 19 Dec 2025 10:27:24 -0500 Subject: [PATCH 233/235] Added black formatting --- orchestrator/migrations/0023_auto_20251210_1043.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/orchestrator/migrations/0023_auto_20251210_1043.py b/orchestrator/migrations/0023_auto_20251210_1043.py index 5dfd89c8..d892cdc5 100644 --- a/orchestrator/migrations/0023_auto_20251210_1043.py +++ b/orchestrator/migrations/0023_auto_20251210_1043.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('orchestrator', '0022_auto_20250805_1406'), + ("orchestrator", "0022_auto_20250805_1406"), ] operations = [ migrations.AlterField( - model_name='job', - name='root_permission', - field=models.CharField(default='750', max_length=4), + model_name="job", + name="root_permission", + field=models.CharField(default="750", max_length=4), ), ] From e85f20f1718ee7c32f2b95b187b9e8b99c61ad87 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 19 Dec 2025 10:30:44 -0500 Subject: [PATCH 234/235] Revert "Set max tasks for periodic cleanup" This reverts commit 75323a94b80a11ef7e170c2be7e88baa35d6fdb6. --- compose.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compose.yaml b/compose.yaml index 0acfac2e..f33cc7a9 100644 --- a/compose.yaml +++ b/compose.yaml @@ -393,7 +393,6 @@ services: -Q ${RIDGEBACK_COMMAND_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_COMMAND_QUEUE}.log \ --pidfile $$PIDFILE \ - --max-tasks-per-child=30 \ --concurrency=30 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_COMMAND_QUEUE} healthcheck: @@ -418,7 +417,6 @@ services: -Q ${RIDGEBACK_ACTION_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_ACTION_QUEUE}.log \ --pidfile $$PIDFILE \ - --max-tasks-per-child=30 \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_ACTION_QUEUE} healthcheck: @@ -442,7 +440,6 @@ services: -Q ${RIDGEBACK_CHECK_STATUS_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_CHECK_STATUS_QUEUE}.log \ --pidfile $$PIDFILE \ - --max-tasks-per-child=30 \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CHECK_STATUS_QUEUE} healthcheck: @@ -466,7 +463,6 @@ services: -Q ${RIDGEBACK_SUBMIT_JOB_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_SUBMIT_JOB_QUEUE}.log \ --pidfile $$PIDFILE \ - --max-tasks-per-child=30 \ --concurrency=5 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SUBMIT_JOB_QUEUE} healthcheck: @@ -490,7 +486,6 @@ services: -Q ${RIDGEBACK_SET_PERMISSIONS_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_SET_PERMISSIONS_QUEUE}.log \ --pidfile $$PIDFILE \ - --max-tasks-per-child=30 \ --concurrency=10 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_SET_PERMISSIONS_QUEUE} healthcheck: @@ -514,7 +509,6 @@ services: -Q ${RIDGEBACK_CLEANUP_QUEUE} \ -f /ridgeback/celery/logs/${RIDGEBACK_CLEANUP_QUEUE}.log \ --pidfile $$PIDFILE \ - --max-tasks-per-child=30 \ --concurrency=2 \ -n ridgeback.${RIDGEBACK_DEPLOYMENT}.${RIDGEBACK_CLEANUP_QUEUE} healthcheck: From f5a9faeb3bddd368f14592fdeb3cf3faf4423b63 Mon Sep 17 00:00:00 2001 From: Nikhil Kumar Date: Fri, 19 Dec 2025 10:31:04 -0500 Subject: [PATCH 235/235] Revert "Set init setting to manage zombie processes" This reverts commit aaa2e2d2ced6809fe33d8782c8ddc77d5d257e17. --- compose.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/compose.yaml b/compose.yaml index f33cc7a9..940e766f 100644 --- a/compose.yaml +++ b/compose.yaml @@ -2,7 +2,6 @@ name: "Ridgeback Services ${RIDGEBACK_DEPLOYMENT}" x-ridgeback_celery: &ridgeback_celery image: mskcc/ridgeback:${RIDGEBACK_VERSION} - init: true restart: always user: "${DOCKER_UID}:${DOCKER_GID}" networks: