Skip to content
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ volumes/
*.key
*.pem
.jwks

# Python
__pycache__/
*.pyc
6 changes: 2 additions & 4 deletions front_end/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ RUN apt-get update && apt-get install -y libmcrypt-dev \
&& docker-php-ext-install gd

RUN apt-get update && apt-get install gnupg curl
RUN curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc |gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
RUN curl -fsSL https://pgp.mongodb.com/server-7.0.asc | gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
RUN echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] http://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 main" | tee /etc/apt/sources.list.d/mongodb-org-7.0.list
RUN apt-get update
RUN apt-get install -y mongodb-org
Expand All @@ -39,9 +39,7 @@ WORKDIR /var/www/html
ADD openVRE /var/www/html/openVRE
RUN cd openVRE
WORKDIR /var/www/html/openVRE
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN composer self-update
RUN composer update --ignore-platform-req=ext-mongodb --ignore-platform-req=ext-mongodb
# Dependencies are pre-seeded in ./openVRE/vendor for offline/reproducible builds.
RUN mkdir logs
RUN touch logs/application.log
RUN chmod -R 777 logs/application.log
Expand Down
594 changes: 594 additions & 0 deletions front_end/openVRE/public/phplib/classes/ProcessK8s.php

Large diffs are not rendered by default.

44 changes: 37 additions & 7 deletions front_end/openVRE/public/phplib/classes/Tooljob.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ public function __construct($tool, $execution = "", $project = "", $descrip = ""
switch ($this->launcher) {
case "SGE":
case "docker_SGE":
case "kubernetes_native":
$this->root_dir_virtual = $GLOBALS['clouds'][$this->cloudName]['dataDir_virtual'] . "/" . $_SESSION['User']['id'];
$this->root_dir_mug = $GLOBALS['clouds'][$this->cloudName]['dataDir_virtual'];
$this->pub_dir_virtual = $GLOBALS['clouds'][$this->cloudName]['pubDir_virtual'];
Expand Down Expand Up @@ -1116,6 +1117,19 @@ public function prepareExecution($tool, $metadata, $metadata_pub = [])

break;

case "kubernetes_native":
$cmd = $this->setBashCmd_SGE($tool);
if (!$cmd) {
return 0;
}

$submissionFilename = $this->createSubmitFile_SGE($cmd);
if (!is_file($submissionFilename)) {
return 0;
}

break;

case "PMES":
$json_data = $this->setPMESrequest($tool);
if (!$json_data) {
Expand Down Expand Up @@ -1395,6 +1409,10 @@ protected function setBashCommandDockerSge($tool)
" --out_metadata " . $this->stageout_file_virtual .
" --log_file " . $this->log_file_virtual;

if (isset($this->launcher) && $this->launcher === "kubernetes_native") {
return $cmd_vre;
}


$cmd = "docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock -d" .
" " . $cmd_envs .
Expand Down Expand Up @@ -1788,6 +1806,7 @@ public function submit($tool)
case "SGE":
case "ega_demo":
case "docker_SGE":
case "kubernetes_native":
return $this->enqueue($tool);
case "Slurm_Singularity":
return $this->enqueue($tool);
Expand All @@ -1808,21 +1827,32 @@ protected function enqueue($tool)
return 0;
}
$jobManager = $launcherInfo['launcher']['job_manager']
?? $tool['infrastructure']['clouds'][$this->cloudName]['launcher'];
?? $tool['infrastructure']['clouds'][$this->cloudName]['launcher'];
$memory = $launcherInfo['memory'] ?? $tool['infrastructure']['memory'];
$cpus = $launcherInfo['cpus'] ?? $tool['infrastructure']['cpus'];
$queue = $launcherInfo['queue'] ?? $tool['infrastructure']['clouds'][$this->cloudName]['queue'];
// error_log("Resolved Parameters: Queue=$queue, CPUs=$cpus, Memory=$memory, jobManager=$jobManager");
$jobOptions = array();
if ($jobManager === "kubernetes_native") {
$jobOptions["image"] = $tool['infrastructure']['container_image'] ?? "";
if ($jobOptions["image"] === "") {
$_SESSION['errorData']['Error'][] = "Missing infrastructure.container_image for kubernetes_native launcher.";
return 0;
}
$jobOptions["env"] = array();
if (isset($tool['infrastructure']['container_env']) && is_array($tool['infrastructure']['container_env'])) {
foreach ($tool['infrastructure']['container_env'] as $env_key => $env_value) {
$jobOptions["env"][$env_key] = (string)$env_value;
}
}
}

list($pid, $errMesg) = execJob($this->working_dir, $this->submission_file, $queue, $cpus, $memory, $this->stdout_file, $this->stderr_file, $jobManager);
list($pid, $errMesg) = execJob($this->working_dir, $this->submission_file, $queue, $cpus, $memory, $this->stdout_file, $this->stderr_file, $jobManager, $this->toolId, $jobOptions);
if (!$pid) {
//error_log($pid, $errMesg, NULL, $this->toolId, $this->cloudName, "SGE", $cpus, $memory);
error_log("Error: $errMesg");
log_addError($pid, $errMesg, NULL, $this->toolId, $this->cloudName, $jobManager, $cpus, $memory);
$_SESSION['errorData']['Error'][] = "Internal error. Cannot enqueue job.";
return 0;
}
#logger("USER:" . $_SESSION['User']['_id'] . ", ID:" . $_SESSION['User']['id'] . ", LAUNCHER:SGE, TOOL:" . $this->toolId . ", PID:$pid");
//error_log("USER:" . $_SESSION['User']['_id'] . ", ID:" . $_SESSION['User']['id'] . ", LAUNCHER:SGE, TOOL:" . $this->toolId . ", PID:$pid");
logger("USER:" . $_SESSION['User']['_id'] . ", ID:" . $_SESSION['User']['id'] . ", LAUNCHER:" . $jobManager . ", TOOL:" . $this->toolId . ", PID:$pid");
log_addSubmission($pid, $this->toolId, $this->cloudName, $jobManager, $cpus, $memory, $this->working_dir);

$this->pid = $pid;
Expand Down
63 changes: 49 additions & 14 deletions front_end/openVRE/public/phplib/processJob.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#


function execJob($workDir, $shFile, $queue, $cpus = 1, $mem = 0, $logFile = "job_output.log", $errFile = "job_error.log", $jobManager = "docker_SGE")
function execJob($workDir, $shFile, $queue, $cpus = 1, $mem = 0, $logFile = "job_output.log", $errFile = "job_error.log", $jobManager = "docker_SGE", $toolId = "", $jobOptions = array())
{
logger("Start job submission via $jobManager");
error_log("DEBUG- execJob: Start job submission via $jobManager");
Expand Down Expand Up @@ -33,15 +33,11 @@ function execJob($workDir, $shFile, $queue, $cpus = 1, $mem = 0, $logFile = "job
$_SESSION['errorData']['Error'][] = "Queue not provided.";
return [0, "Queue not provided."];
}


$queue = (isset($queue) ? $queue : $GLOBALS['queueTask']);
$jobname = $_SESSION['User']['id'] . "#" . basename($shFile);

//
// Start SGE process
//$process = new ProcessSGE($shFile, $workDir, $queue, $jobname, $cpus, $mem, $logFile, $errFile);

switch ($jobManager) {
case "docker_SGE":
error_log("DEBUG: Submitting job via docker_SGE. Parameters: shFile=$shFile, workDir=$workDir, queue=$queue, jobname=$jobname, cpus=$cpus, mem=$mem, logFile=$logFile, errFile=$errFile");
Expand All @@ -52,14 +48,31 @@ function execJob($workDir, $shFile, $queue, $cpus = 1, $mem = 0, $logFile = "job
error_log("DEBUG: Submitting job via Slurm to $remote_system. Parameters: shFile=$shFile, workDir=$workDir, logFile=$logFile, errFile=$errFile");
$process = new ProcessSlurm($shFile, $workDir, $logFile, $errFile, $remote_system);
break;
case "kubernetes_native":
$schedUrl = getenv("OPENVRE_K8S_SCHEDULER_URL") ?: "";
$schedHost = $schedUrl !== ""
? (string)(parse_url($schedUrl, PHP_URL_HOST) ?: "(parse_failed)")
: "(not_set)";
$k8sNs = getenv("OPENVRE_K8S_NAMESPACE") ?: "(env_unset)";
$jobOptKeys = is_array($jobOptions) && count($jobOptions)
? implode(",", array_keys($jobOptions))
: "(none)";
error_log(
"DEBUG: Submitting job via kubernetes_native. Parameters: shFile=$shFile, workDir=$workDir, queue=$queue, "
. "jobname=$jobname, cpus=$cpus, mem=$mem, logFile=$logFile, errFile=$errFile, "
. "namespace=$k8sNs, scheduler_host=$schedHost, jobOptions_keys=$jobOptKeys"
);
require_once __DIR__ . "/classes/ProcessK8s.php";
$process = new ProcessK8s($shFile, $workDir, $queue, $jobname, $cpus, $mem, $logFile, $errFile, $jobOptions);
break;
default:
$process = new ProcessSGE($shFile, $workDir, $queue, $jobname, $cpus, $mem, $logFile, $errFile);
break;
break;
}

if (!$process->status()) {
$_SESSION['errorData']['Error'][] = "Job submission failed.<br/>" . $process->getFullCommand() . "<br/>" . $process->getErr();
$errMesg = "ERROR: Job submission failed. FullCommand: '" . $process->getFullCommand() . "'. ErrorSGE: '" . $process->getErr() . "'";
$errMesg = "ERROR: Job submission failed. FullCommand: '" . $process->getFullCommand() . "'. ErrorDetail: '" . $process->getErr() . "'";
logger($errMesg);
return array(0, $errMesg);
}
Expand Down Expand Up @@ -140,18 +153,30 @@ function getRunningJobInfo($pid, $launcherType = NULL, $cloudName = "local")

// guess launcher
if (!$launcherType) {
if (is_numeric($pid))
if (is_numeric($pid)) {
$launcherType = "SGE";
else
} elseif (strpos((string)$pid, "-") !== false) {
$launcherType = "kubernetes_native";
} else {
$launcherType = "PMES";
}
}

logger("getRunningJobInfo: launcherType = $launcherType");

// create new jobProcess
if ($launcherType == "SGE" || $launcherType == "docker_SGE") {
$process = new ProcessSGE();
$job = $process->getRunningJobInfo($pid);
} elseif ($launcherType == "kubernetes_native") {
require_once __DIR__ . "/classes/ProcessK8s.php";
$process = new ProcessK8s();
$job = $process->getRunningJobInfo($pid);
$k8sSummary = empty($job)
? "empty (job finished or unknown to scheduler)"
: ("keys=" . implode(",", array_keys($job))
. (isset($job['state']) ? " state=" . $job['state'] : ""));
error_log("DEBUG: getRunningJobInfo kubernetes_native pid=$pid $k8sSummary");
logger("getRunningJobInfo (kubernetes_native): pid=$pid $k8sSummary");
} elseif ($launcherType == "PMES") {
$process = new ProcessPMES($cloudName);
$job = $process->getRunningJobInfo($pid);
Expand All @@ -164,9 +189,7 @@ function getRunningJobInfo($pid, $launcherType = NULL, $cloudName = "local")
$_SESSION['errorData']['Error'][] = "Cannot monitor job '$pid' of type '$launcherType'. Launcher not implemented.";
return $job;
}

logger("getRunningJobInfo: end processing $pid");

// return job info
return $job;
}
Expand Down Expand Up @@ -304,6 +327,8 @@ function delJob($pid, $launcherType = NULL, $cloudName = "local", $login = NULL)
if (!$launcherType) {
if (is_numeric($pid)) {
$launcherType = "docker_SGE";
} elseif (strpos((string)$pid, "-") !== false) {
$launcherType = "kubernetes_native";
} else {
$launcherType = "PMES";
}
Expand All @@ -321,6 +346,16 @@ function delJob($pid, $launcherType = NULL, $cloudName = "local", $login = NULL)
updateLogFromJobInfo($jobInfo['log'], $pid, $launcherType, $cloudName);
// Add any other file redirection logic here
}
} elseif ($launcherType == "kubernetes_native") {
error_log("DEBUG: delJob kubernetes_native pid=$pid calling ProcessK8s::stop");
require_once __DIR__ . "/classes/ProcessK8s.php";
$processK8s = new ProcessK8s();
list($r_sge, $msg_sge) = $processK8s->stop($pid);
error_log(
"DEBUG: delJob kubernetes_native pid=$pid stop_ok=" . ($r_sge ? "1" : "0")
. " msg=" . $msg_sge
);
logger("delJob (kubernetes_native): pid=$pid stop_ok=" . ($r_sge ? "1" : "0") . " msg=" . $msg_sge);
} elseif ($launcherType == "PMES") {
$process = new ProcessPMES();
$r = $process->stop($pid);
Expand Down
6 changes: 5 additions & 1 deletion front_end/openVRE/public/phplib/projects.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -2347,7 +2347,7 @@ function resolvePath_toLocalAbsolutePath($path, $job)
$rfn = str_replace($job['root_dir_virtual'], $GLOBALS['dataDir'] . $_SESSION['User']['id'], $path);

//SGE finds mounted dataDir as root_dir_virtual
} elseif ($job['launcher'] == "SGE" || $job['launcher'] == "ega_demo" || $job['launcher'] == "docker_SGE") {
} elseif ($job['launcher'] == "SGE" || $job['launcher'] == "ega_demo" || $job['launcher'] == "docker_SGE" || $job['launcher'] == "kubernetes_native") {
$rfn = str_replace($job['root_dir_mug'], $GLOBALS['dataDir'], $path);
}
// direct from file_path
Expand Down Expand Up @@ -2380,6 +2380,10 @@ function resolvePath_toLocalAbsolutePath($path, $job)
}
}
//clean slashes
if ($rfn === "") {
// Keep original absolute path instead of returning empty when launcher mapping is missing.
$rfn = $path;
}
$rfn = preg_replace('#/+#', '/', $rfn);

//return absolute path
Expand Down
12 changes: 12 additions & 0 deletions scheduler/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.git
.gitignore
__pycache__/
*.pyc
*.pyo
*.pyd
.pytest_cache/
.mypy_cache/
.ruff_cache/
dist/
build/
*.egg-info/
11 changes: 11 additions & 0 deletions scheduler/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM python:3.11-alpine

WORKDIR /app

COPY app.py /app/app.py

EXPOSE 8080

USER 65534:65534

CMD ["python", "/app/app.py"]
Loading