Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First stab at implementing asynchronously pushed-back task results #2

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion assets/components/sitedashclient/pull.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@
// Make sure the params are sanitized
$params = $modx::sanitize($_POST);

$pusher = null;
if (array_key_exists('_return_push', $_POST) && !empty($_POST['_return_push'])) {
$server = $modx->getOption('sitedash.server_uri', null, 'https://sitedash.app/', true);
$responseUri = (string)$_POST['_return_push'];

$pusher = new \modmore\SiteDashClient\Communication\Pusher($server, $responseUri);
}

switch ($params['request']) {
case 'system':
case 'system/refresh':
Expand Down Expand Up @@ -96,7 +104,7 @@


case 'upgrade/backup':
$cmd = new \modmore\SiteDashClient\Upgrade\Backup($modx);
$cmd = new \modmore\SiteDashClient\Upgrade\Backup($modx, $pusher);
$cmd->run();
break;

Expand All @@ -114,6 +122,11 @@
$cmd->run();
break;

case 'communication/test-async-push':
$cmd = new \modmore\SiteDashClient\Communication\TestAsyncPush($pusher);
$cmd->run();
break;

default:
http_response_code(400);
echo json_encode([
Expand Down
90 changes: 90 additions & 0 deletions core/components/sitedashclient/src/Communication/Pusher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace modmore\SiteDashClient\Communication;

final class Pusher {
private $responseUri;

public function __construct($server, $responseUri)
{
$this->responseUri = $server . $responseUri;
}

public function acknowledge()
{
ob_start();

echo json_encode([
'return_push' => true,
]);

// Get the size of the output.
$size = ob_get_length();

// 202 accepted
http_response_code(202);

// Disable compression (in case content length is compressed).
header('Content-Encoding: none');

// Set the content length of the response.
header("Content-Length: {$size}");

// Close the connection.
header('Connection: close');

// Flush all output.
ob_end_flush();
ob_flush();
flush();

ignore_user_abort(true);
@session_write_close();

if (is_callable('fastcgi_finish_request')) {
fastcgi_finish_request();
return;
}
sleep(1);
}

public function push(array $data)
{
$logFile = MODX_CORE_PATH . 'cache/logs/sitedash_push_' . date('Y-m-d-H-i-s') . '.log';

$ch = curl_init();

$postData = $this->prepareData($data);
curl_setopt($ch, CURLOPT_URL, $this->responseUri);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type:application/json']);

$response = curl_exec($ch);
$error = curl_error($ch);
$errno = curl_errno($ch);
curl_close($ch);

$dataFormat = json_encode($data, JSON_PRETTY_PRINT);
$postDataFormat = json_encode($postData, JSON_PRETTY_PRINT);
$log = <<<HTML
Push request to {$this->responseUri}:

Data: {$dataFormat}

Data to post to SiteDash, incl signature: {$postDataFormat}

Response from SiteDash: {$errno} {$error}

{$response}
HTML;

file_put_contents($logFile, $log);
}

private function prepareData(array $data)
{
return $data;
}
}
28 changes: 28 additions & 0 deletions core/components/sitedashclient/src/Communication/Result.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace modmore\SiteDashClient\Communication;

final class Result {
/**
* @var Pusher|null
*/
private $pusher;

public function __construct(Pusher $pusher = null)
{
$this->pusher = $pusher;
}

public function __invoke($responseCode, $data)
{
if ($this->pusher) {
$this->pusher->push($data);
}
else {
http_response_code($responseCode);
echo json_encode($data, JSON_PRETTY_PRINT);
@session_write_close();
exit();
}
}
}
38 changes: 38 additions & 0 deletions core/components/sitedashclient/src/Communication/TestAsyncPush.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace modmore\SiteDashClient\Communication;

use modmore\SiteDashClient\CommandInterface;

class TestAsyncPush implements CommandInterface {
/**
* @var Pusher
*/
private $pusher;

public function __construct($pusher)
{
$this->pusher = $pusher;
}

public function run()
{
$this->pusher->acknowledge();
$result = new Result($this->pusher);

$logFile = MODX_CORE_PATH . 'cache/logs/sitedash_pushtest_' . date('Y-m-d-H-i-s') . '.log';
$limit = 15;
$i = 0;
@set_time_limit($limit + 5);
while ($i < $limit) {
$i++;
file_put_contents($logFile, date('Y-m-d H:i:s') . "\n", FILE_APPEND);
sleep(1);
}

$result(200, [
'success' => true,
'counted_to' => $limit,
]);
}
}
2 changes: 1 addition & 1 deletion core/components/sitedashclient/src/Refresh.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function run()
$data = [];
$data['client'] = \SiteDashClient::VERSION;
$data['client_options'] = [
'supports_return_push' => false,
'supports_return_push' => true,
'supports_async_execute' => false,
];
$data['modx'] = $this->getMODXData();
Expand Down
42 changes: 26 additions & 16 deletions core/components/sitedashclient/src/Upgrade/Backup.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
namespace modmore\SiteDashClient\Upgrade;

use modmore\SiteDashClient\CommandInterface;
use modmore\SiteDashClient\Communication\Pusher;
use modmore\SiteDashClient\Communication\Result;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;

class Backup implements CommandInterface {
protected $modx;
protected $files = [];
protected $targetDirectory;
/**
* @var Pusher|null
*/
private $pusher;

public function __construct(\modX $modx)
public function __construct(\modX $modx, $pusher = null)
{
$this->modx = $modx;
$this->pusher = $pusher;

$this->files = [
MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php',
Expand Down Expand Up @@ -66,6 +73,13 @@ public function run()
return;
}

// If a push result was requested, send an ack response and continue processing
if ($this->pusher) {
$this->pusher->acknowledge();
}

$result = new Result($this->pusher);

/**
* Include the config file to access the database information
*
Expand Down Expand Up @@ -112,51 +126,48 @@ public function run()
$msg = str_replace($password_parameter, '-p\'<PASS>\'', $msg);
$trace = $e->getTraceAsString();
$trace = str_replace($password_parameter, '-p\'<PASS>\'', $trace);
http_response_code(503);
echo json_encode([

$result(503, [
'success' => false,
'message' => 'Received an error trying to run mysqlbackup: ' . $msg,
'binary' => $mysqldump,
'directory' => str_replace(MODX_CORE_PATH, '{core_path}', $this->targetDirectory),
'output' => $trace,
], JSON_PRETTY_PRINT);
]);
return;
}
$output = $backupProcess->getErrorOutput() . ' ' . $backupProcess->getOutput();
$output = str_replace($password_parameter, '-p\'<PASS>\'', $output);
if (!$backupProcess->isSuccessful()) {
http_response_code(503);
$code = $backupProcess->getExitCode();
if ($code === 127) {
echo json_encode([
$result(503, [
'success' => false,
'message' => 'Could not find the mysqldump program on your server; please configure the sitedashclient.mysqldump_binary system setting to point to mysqldump to create backups.',
'binary' => $mysqldump,
'directory' => str_replace(MODX_CORE_PATH, '{core_path}', $this->targetDirectory),
'output' => $output,
], JSON_PRETTY_PRINT);
]);
return;
}

echo json_encode([
$result(503, [
'success' => false,
'message' => 'Received exit code ' . $code . ' trying to create a database backup using ' . $mysqldump . ' with message: ' . $output,
'output' => $output,
'return' => $code,
], JSON_PRETTY_PRINT);
]);
return;
}

$backupSize = filesize($targetFile);
if ($backupSize < 150 * 1024) { // a clean install is ~ 200kb, so we ask for at least 150
http_response_code(503);

echo json_encode([
$result(503, [
'success' => false,
'message' => 'While the backup with ' . $mysqldump . ' did not indicate an error, the mysql backup is only ' . number_format($backupSize / 1024, 0) . 'kb in size, so it probably failed.',
'output' => $output,
'return' => $backupProcess->getExitCode(),
], JSON_PRETTY_PRINT);
]);
return;
}

Expand All @@ -179,11 +190,10 @@ public function run()
}
}

http_response_code(200);
echo json_encode([
$result(200, [
'success' => true,
'directory' => str_replace(MODX_CORE_PATH, '', $this->targetDirectory),
], JSON_PRETTY_PRINT);
]);
}

private function createDirectory($target)
Expand Down