Skip to content

Commit

Permalink
Add Load-balancing cookie support (#206)
Browse files Browse the repository at this point in the history
These changes support AWS load-balancer stickiness,
by saving and sending back the cookie set by the load balancer
using curl.
  • Loading branch information
AnupamaSarjoshi authored Mar 20, 2024
1 parent b1f2d7c commit d7ba97b
Showing 1 changed file with 34 additions and 20 deletions.
54 changes: 34 additions & 20 deletions classes/jobesandbox.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class qtype_coderunner_jobesandbox extends qtype_coderunner_sandbox {
* - with haproxy try "balance hdr(X-CodeRunner-Job-Id)" (not tested)
* - with a Netscaler use rule-based persistence with expression
* HTTP.REQ.HEADER(“X-CodeRunner-Job-Id”)
* - with cookies support
*/
private $currentjobid = null;

Expand Down Expand Up @@ -137,6 +138,8 @@ public function get_languages() {
*/

public function execute($sourcecode, $language, $input, $files = null, $params = null) {
global $CFG;

$language = strtolower($language);
if (is_null($input)) {
$input = '';
Expand Down Expand Up @@ -202,21 +205,36 @@ public function execute($sourcecode, $language, $input, $files = null, $params =
$postbody = ['run_spec' => $runspec];
$this->currentjobid = sprintf('%08x', mt_rand());

// Create a single curl object here, with support for cookies, and use it for all requests.
// This supports Jobe back-ends that use cookies for sticky load-balancing.
// Make a place to store the cookies.
make_temp_directory('qtype_coderunner');
$cookiefile = $CFG->tempdir . '/qtype_coderunner/session_cookies_' . $this->currentjobid . '.txt';

$curl = new curl();
$curl->setopt([
'CURLOPT_COOKIEJAR' => $cookiefile,
'CURLOPT_COOKIEFILE' => $cookiefile,
]);

// Try submitting the job. If we get a 404, try again after
// putting all the files on the server. Anything else is an error.
$httpcode = $this->submit($postbody);
$httpcode = $this->submit($postbody, $curl);
if ($httpcode == 404) { // If it's a file not found error ...
foreach ($files as $filename => $contents) {
if (($httpcode = $this->put_file($contents)) != 204) {
if (($httpcode = $this->put_file($contents, $curl)) != 204) {
break;
}
}
if ($httpcode == 204) {
// Try again if put_files all worked.
$httpcode = $this->submit($postbody);
$httpcode = $this->submit($postbody, $curl);
}
}

// Delete the cookie file.
unlink($cookiefile);

$runresult = [];
$runresult['sandboxinfo'] = [
'jobeserver' => $this->jobeserver,
Expand Down Expand Up @@ -328,28 +346,20 @@ private function get_error_code($httpcode) {
}

// Put the given file to the server, using its MD5 checksum as the id.
// If you pass a curl object, this will be used to make the request.
// Returns the HTTP response code, or -1 if the HTTP request fails
// altogether.
// Moodle curl class doesn't support an appropriate form of PUT so
// we use raw PHP curl here.
private function put_file($contents) {
private function put_file($contents, $curl) {
$id = md5($contents);
$contentsb64 = base64_encode($contents);
$resource = "files/$id";

[$url, $headers] = $this->get_jobe_connection_info($resource);

$body = ['file_contents' => $contentsb64];
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($body));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
$info = curl_getinfo($curl);
curl_close($curl);
return $result === false ? -1 : $info['http_code'];
$result = $curl->put($url, json_encode($body));
$returncode = $curl->info['http_code'];
return $result === false ? -1 : $returncode;
}

/**
Expand Down Expand Up @@ -403,8 +413,9 @@ private function get_jobe_connection_info($resource) {
// response was 400 Bad Parameter.
// We don't at this stage deal with Jobe servers that may defer requests
// i.e. that return 202 Accepted rather than 200 OK.
private function submit($job) {
[$returncode, $response] = $this->http_request('runs', self::HTTP_POST, $job);
// If you pass a curl object, this will be used to make the request.
private function submit($job, $curl) {
[$returncode, $response] = $this->http_request('runs', self::HTTP_POST, $job, $curl);
$this->response = $response;
return $returncode;
}
Expand All @@ -419,10 +430,13 @@ private function submit($job) {
// Note that the Moodle curl class documentation lies when it says the
// return value from get and post is a bool. It's either the value false
// if the request failed or the actual string response, otherwise.
private function http_request($resource, $method, $body = null) {
// If you pass a curl object, this will be used to make the request.
private function http_request($resource, $method, $body = null, $curl = null) {
[$url, $headers] = $this->get_jobe_connection_info($resource);

$curl = new curl();
if ($curl == null) {
$curl = new curl();
}
$curl->setHeader($headers);

if ($method === self::HTTP_GET) {
Expand Down

0 comments on commit d7ba97b

Please sign in to comment.