This commit is contained in:
Julio Cesar Laura Avendaño
2019-04-04 14:44:33 -04:00
parent 90a7b778a1
commit 0da9afd8d3
6 changed files with 350 additions and 3 deletions

View File

@@ -519,13 +519,15 @@ class database extends database_base
}
/**
* execute a sql query
* Execute a sql query
*
* @param string $query
* @param bool $throwError
*
* @return array
* @throws Exception
*/
public function executeQuery($query)
public function executeQuery($query, $throwError = false)
{
$this->logQuery($query);
@@ -545,9 +547,13 @@ class database extends database_base
return $result;
} catch (Exception $exception) {
$this->logQuery($exception->getMessage());
if ($throwError) {
throw $exception;
} else {
return [];
}
}
}
/**
* close the current connection

View File

@@ -36,6 +36,8 @@ CLI::taskArg('workspace');
/*----------------------------------********---------------------------------*/
CLI::taskRun("run_unify_database");
/*----------------------------------********---------------------------------*/
CLI::taskName('upgrade-query');
CLI::taskRun('runUpgradeQuery');
/**
* Execute the upgrade
@@ -299,3 +301,25 @@ function run_unify_database($args)
$flag = G::isPMUnderUpdating(0);
}
/*----------------------------------********---------------------------------*/
/**
* Execute a query, used internally for upgrade process
*
* @param array $options
*/
function runUpgradeQuery($options)
{
// Initializing variables
$workspaceName = $options[0];
$query = base64_decode($options[1]);
$isRbac = (bool)$options[2];
// Creating a new instance of the extended class
$workspace = new WorkspaceTools($workspaceName);
// Execute the query
$workspace->upgradeQuery($query, $isRbac);
// Terminate without error
exit('success');
}

View File

@@ -4623,4 +4623,16 @@ class WorkspaceTools
}
}
}
/**
* Execute a query, used internally for the upgrade process
*
* @param string $query
* @param bool $rbac
*/
public function upgradeQuery($query, $rbac)
{
$database = $this->getDatabase($rbac);
$database->executeQuery($query, true);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace ProcessMaker\Core;
/**
* Class to manage the processes that runs in the shell
*/
class ProcessesManager
{
// Class properties
private $processes;
private $sleepTime = 1;
private $terminated = [];
private $errors = [];
/**
* Class constructor
*
* @param array $processes
*/
public function __construct(array $processes)
{
$this->processes = $processes;
}
/**
* Get the list of terminated processes
*
* @return array
*/
public function getTerminated()
{
return $this->terminated;
}
/**
* Get the list of processes with errors
*
* @return array
*/
public function getErrors()
{
return $this->errors;
}
/**
* Set the sleep time after each statuses revision
*
* @param int $sleepTime
*/
public function setSleepTime($sleepTime)
{
$this->sleepTime = $sleepTime;
}
/**
* Run the processes
*/
public function run()
{
// Start all processes
foreach ($this->processes as $process) {
$process->run();
}
// Manage the processes
$this->manage();
}
/**
* Manage all started processes
*/
private function manage()
{
do {
// Check all remaining processes
foreach ($this->processes as $index => $process) {
// If the process has finished, save the info and destroy it
if ($process->getStatus() === RunProcess::TERMINATED || $process->getStatus() === RunProcess::ERROR) {
$processInfo = ['command' => $process->getCommand(), 'rawAnswer' => $process->getRawAnswer()];
if ($process->getStatus() === RunProcess::TERMINATED) {
// Processes completed successfully
$this->terminated[] = $processInfo;
} else {
// Processes completed with errors
$this->errors[] = $processInfo;
}
// Destroy the process
unset($this->processes[$index]);
}
}
// Waiting...
sleep($this->sleepTime);
} while (!empty($this->processes));
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace ProcessMaker\Core;
/**
* This class run a command in shell and stores the pointer to him
*/
class RunProcess
{
// Class constants
const TERMINATED = 'terminated';
const RUNNING = 'running';
const NOT_RUNNING = 'not_running';
const ERROR = 'error';
// This constant can be overrides in the child class according to the command response, always should be have a value
const EXPECTED_ANSWER = '1';
// Class properties
private $resource;
private $command;
private $rawAnswer;
private $status;
private $exitCode;
private $pipes;
private $descriptors = [
['pipe', 'r'],
['pipe', 'w'],
['pipe', 'w']
];
/**
* Class constructor
*
* @param string $command
*/
public function __construct($command)
{
$this->command = $command;
}
/**
* Class destructor, the resource created should be closed
*/
public function __destruct()
{
if (is_resource($this->resource)) {
proc_close($this->resource);
}
}
/**
* Get the command
*
* @return string
*/
public function getCommand()
{
return $this->command;
}
/**
* Get the raw response
*
* @return string|null
*/
public function getRawAnswer()
{
return $this->rawAnswer;
}
/**
* Get the status
*
* @return string
*/
public function getStatus()
{
// If already exist a status return this value
if ($this->status !== null) {
return $this->status;
}
// If doesn't exists a resource the process is not running
if (!is_resource($this->resource)) {
return self::NOT_RUNNING;
}
// If the process is running return this value
if ($this->isRunning()) {
return self::RUNNING;
}
// If the process is not running, parse the response to determine the status
$this->rawAnswer = stream_get_contents($this->pipes[1]);
$this->status = $this->parseAnswer();
return $this->status;
}
/**
* Get the exit code
*
* @return string|null
*/
public function getExitCode()
{
return $this->exitCode;
}
/**
* Run the command
*/
public function run()
{
$this->resource = proc_open($this->command, $this->descriptors, $this->pipes);
}
/**
* Process is running?
*
* @return bool
*/
public function isRunning()
{
// Get the process status
$status = proc_get_status($this->resource);
// If process is not running get the exit code
if ($status['running'] === false) {
$this->exitCode = $status['exitcode'];
}
return $status['running'];
}
/**
* Process the raw response and compare with the expected answer in order to determine the status
*
* @return string
*/
public function parseAnswer()
{
return $this->rawAnswer === self::EXPECTED_ANSWER ? self::TERMINATED : self::ERROR;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace ProcessMaker\Upgrade;
use ProcessMaker\Core\RunProcess;
/**
* Extended class to manage the processes that executes a queries in the upgrade process
*/
class RunProcessUpgradeQuery extends RunProcess
{
// Class constants
const SUCCESS = 'success';
const CMD = PHP_BINARY . ' processmaker upgrade-query %s %s %s';
const RBAC = '1';
const NO_RBAC = '0';
// Class properties
private $workspace;
private $sql;
private $isRbac;
/**
* Class constructor
*
* @param string $workspace
* @param string $sql
* @param bool $isRbac
*/
public function __construct($workspace, $sql, $isRbac = false)
{
// Set properties values
$this->workspace = $workspace;
$this->sql = $sql;
$this->isRbac = $isRbac;
// Build the command and send to the parent class
parent::__construct($this->buildCommand());
}
/**
* Override the parent method in order to compare the raw response with the SUCCESS value
*
* @return string
*/
public function parseAnswer()
{
return $this->getRawAnswer() === self::SUCCESS ? parent::TERMINATED : parent::ERROR;
}
/**
* Build the command to execute a query for the upgrade process
*
* @return string
*/
private function buildCommand()
{
return sprintf(self::CMD, $this->workspace, base64_encode($this->sql),
($this->isRbac ? self::RBAC : self::NO_RBAC));
}
}