1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-07 13:21:02 +01:00

(stable) Promote 2019 Week 26

This commit is contained in:
epriestley 2019-06-28 17:38:58 -07:00
commit 4c242256e4
10 changed files with 202 additions and 455 deletions

View file

@ -10,7 +10,7 @@ return array(
'conpherence.pkg.css' => '3c8a0668',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => 'af983028',
'core.pkg.js' => '8225dc58',
'core.pkg.js' => '5a792749',
'differential.pkg.css' => '8d8360fb',
'differential.pkg.js' => '67e02996',
'diffusion.pkg.css' => '42c75c37',
@ -253,7 +253,7 @@ return array(
'rsrc/externals/javelin/lib/URI.js' => '2e255291',
'rsrc/externals/javelin/lib/Vector.js' => 'e9c80beb',
'rsrc/externals/javelin/lib/WebSocket.js' => 'fdc13e4e',
'rsrc/externals/javelin/lib/Workflow.js' => '851f642d',
'rsrc/externals/javelin/lib/Workflow.js' => '445e21a8',
'rsrc/externals/javelin/lib/__tests__/Cookie.js' => 'ca686f71',
'rsrc/externals/javelin/lib/__tests__/DOM.js' => '4566e249',
'rsrc/externals/javelin/lib/__tests__/JSON.js' => '710377ae',
@ -752,7 +752,7 @@ return array(
'javelin-workboard-header' => '111bfd2d',
'javelin-workboard-header-template' => 'ebe83a6b',
'javelin-workboard-order-template' => '03e8891f',
'javelin-workflow' => '851f642d',
'javelin-workflow' => '445e21a8',
'maniphest-report-css' => '3d53188b',
'maniphest-task-edit-css' => '272daa84',
'maniphest-task-summary-css' => '61d1667e',
@ -1294,6 +1294,17 @@ return array(
'43bc9360' => array(
'javelin-install',
),
'445e21a8' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'46116c01' => array(
'javelin-request',
'javelin-behavior',
@ -1607,17 +1618,6 @@ return array(
'javelin-resource',
'javelin-routable',
),
'851f642d' => array(
'javelin-stratcom',
'javelin-request',
'javelin-dom',
'javelin-vector',
'javelin-install',
'javelin-util',
'javelin-mask',
'javelin-uri',
'javelin-routable',
),
'87428eb2' => array(
'javelin-behavior',
'javelin-diffusion-locate-file-source',

View file

@ -536,6 +536,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
'differential.whitespace-matters' => pht(
'Whitespace rendering is now handled automatically.'),
'phd.pid-directory' => pht(
'Phabricator daemons no longer use PID files.'),
);
return $ancient_config;

View file

@ -21,10 +21,6 @@ final class PhabricatorPHDConfigOptions
public function getOptions() {
return array(
$this->newOption('phd.pid-directory', 'string', '/var/tmp/phd/pid')
->setLocked(true)
->setDescription(
pht('Directory that phd should use to track running daemons.')),
$this->newOption('phd.log-directory', 'string', '/var/tmp/phd/log')
->setLocked(true)
->setDescription(

View file

@ -6,7 +6,10 @@ final class PhabricatorDaemonManagementRestartWorkflow
protected function didConstruct() {
$this
->setName('restart')
->setSynopsis(pht('Stop, then start the standard daemon loadout.'))
->setSynopsis(
pht(
'Stop daemon processes on this host, then start the standard '.
'daemon loadout.'))
->setArguments(
array(
array(
@ -17,17 +20,15 @@ final class PhabricatorDaemonManagementRestartWorkflow
'seconds. Defaults to __15__ seconds.'),
'default' => 15,
),
array(
'name' => 'gently',
'help' => pht(
'Ignore running processes that look like daemons but do not '.
'have corresponding PID files.'),
),
array(
'name' => 'force',
'help' => pht(
'Also stop running processes that look like daemons but do '.
'not have corresponding PID files.'),
'Stop all daemon processes on this host, even if they belong '.
'to another Phabricator instance.'),
),
array(
'name' => 'gently',
'help' => pht('Deprecated. Has no effect.'),
),
$this->getAutoscaleReserveArgument(),
));
@ -35,12 +36,11 @@ final class PhabricatorDaemonManagementRestartWorkflow
public function execute(PhutilArgumentParser $args) {
$err = $this->executeStopCommand(
array(),
array(
'graceful' => $args->getArg('graceful'),
'force' => $args->getArg('force'),
'gently' => $args->getArg('gently'),
));
if ($err) {
return $err;
}

View file

@ -6,101 +6,52 @@ final class PhabricatorDaemonManagementStatusWorkflow
protected function didConstruct() {
$this
->setName('status')
->setSynopsis(pht('Show status of running daemons.'))
->setArguments(
array(
array(
'name' => 'local',
'help' => pht('Show only local daemons.'),
),
));
->setSynopsis(pht('Show daemon processes on this host.'));
}
public function execute(PhutilArgumentParser $args) {
$console = PhutilConsole::getConsole();
$process_refs = $this->getOverseerProcessRefs();
if ($args->getArg('local')) {
$daemons = $this->loadRunningDaemons();
} else {
$daemons = $this->loadAllRunningDaemons();
}
if (!$process_refs) {
$instance = $this->getInstance();
if ($instance !== null) {
$this->logInfo(
pht('NO DAEMONS'),
pht(
'There are no running daemon processes for the current '.
'instance ("%s").',
$instance));
} else {
$this->writeInfo(
pht('NO DAEMONS'),
pht('There are no running daemon processes.'));
}
if (!$daemons) {
$console->writeErr(
"%s\n",
pht('There are no running Phabricator daemons.'));
return 1;
}
$status = 0;
$table = id(new PhutilConsoleTable())
->addColumns(array(
'id' => array(
'title' => pht('Log'),
),
'daemonID' => array(
'title' => pht('Daemon'),
),
'host' => array(
'title' => pht('Host'),
),
'pid' => array(
'title' => pht('Overseer'),
),
'started' => array(
'title' => pht('Started'),
),
'daemon' => array(
'title' => pht('Class'),
),
'argv' => array(
'title' => pht('Arguments'),
),
));
foreach ($daemons as $daemon) {
if ($daemon instanceof PhabricatorDaemonLog) {
$table->addRow(array(
'id' => $daemon->getID(),
'daemonID' => $daemon->getDaemonID(),
'host' => $daemon->getHost(),
'pid' => $daemon->getPID(),
'started' => date('M j Y, g:i:s A', $daemon->getDateCreated()),
'daemon' => $daemon->getDaemon(),
'argv' => csprintf('%LR', $daemon->getExplicitArgv()),
->addColumns(
array(
'pid' => array(
'title' => pht('PID'),
),
'command' => array(
'title' => pht('Command'),
),
));
} else if ($daemon instanceof PhabricatorDaemonReference) {
$name = $daemon->getName();
if (!$daemon->isRunning()) {
$daemon->updateStatus(PhabricatorDaemonLog::STATUS_DEAD);
$status = 2;
$name = pht('<DEAD> %s', $name);
}
$daemon_log = $daemon->getDaemonLog();
$id = null;
$daemon_id = null;
if ($daemon_log) {
$id = $daemon_log->getID();
$daemon_id = $daemon_log->getDaemonID();
}
$table->addRow(array(
'id' => $id,
'daemonID' => $daemon_id,
'host' => 'localhost',
'pid' => $daemon->getPID(),
'started' => $daemon->getEpochStarted()
? date('M j Y, g:i:s A', $daemon->getEpochStarted())
: null,
'daemon' => $name,
'argv' => csprintf('%LR', $daemon->getArgv()),
foreach ($process_refs as $process_ref) {
$table->addRow(
array(
'pid' => $process_ref->getPID(),
'command' => $process_ref->getCommand(),
));
}
}
$table->draw();
return 0;
}
}

View file

@ -6,11 +6,7 @@ final class PhabricatorDaemonManagementStopWorkflow
protected function didConstruct() {
$this
->setName('stop')
->setSynopsis(
pht(
'Stop all running daemons, or specific daemons identified by PIDs. '.
'Use **%s** to find PIDs.',
'phd status'))
->setSynopsis(pht('Stop daemon processes on this host.'))
->setArguments(
array(
array(
@ -24,29 +20,21 @@ final class PhabricatorDaemonManagementStopWorkflow
array(
'name' => 'force',
'help' => pht(
'Also stop running processes that look like daemons but do '.
'not have corresponding PID files.'),
'Stop all daemon processes on this host, even if they belong '.
'to another Phabricator instance.'),
),
array(
'name' => 'gently',
'help' => pht(
'Ignore running processes that look like daemons but do not '.
'have corresponding PID files.'),
),
array(
'name' => 'pids',
'wildcard' => true,
'help' => pht('Deprecated. Has no effect.'),
),
));
}
public function execute(PhutilArgumentParser $args) {
return $this->executeStopCommand(
$args->getArg('pids'),
array(
'graceful' => $args->getArg('graceful'),
'force' => $args->getArg('force'),
'gently' => $args->getArg('gently'),
));
}

View file

@ -12,11 +12,6 @@ abstract class PhabricatorDaemonManagementWorkflow
->selectSymbolsWithoutLoading();
}
final protected function getPIDDirectory() {
$path = PhabricatorEnv::getEnvConfig('phd.pid-directory');
return $this->getControlDirectory($path);
}
final protected function getLogDirectory() {
$path = PhabricatorEnv::getEnvConfig('phd.log-directory');
return $this->getControlDirectory($path);
@ -30,56 +25,16 @@ abstract class PhabricatorDaemonManagementWorkflow
pht(
"%s requires the directory '%s' to exist, but it does not exist ".
"and could not be created. Create this directory or update ".
"'%s' / '%s' in your configuration to point to an existing ".
"'%s' in your configuration to point to an existing ".
"directory.",
'phd',
$path,
'phd.pid-directory',
'phd.log-directory'));
}
}
return $path;
}
final protected function loadRunningDaemons() {
$daemons = array();
$pid_dir = $this->getPIDDirectory();
$pid_files = Filesystem::listDirectory($pid_dir);
foreach ($pid_files as $pid_file) {
$path = $pid_dir.'/'.$pid_file;
$daemons[] = PhabricatorDaemonReference::loadReferencesFromFile($path);
}
return array_mergev($daemons);
}
final protected function loadAllRunningDaemons() {
$local_daemons = $this->loadRunningDaemons();
$local_ids = array();
foreach ($local_daemons as $daemon) {
$daemon_log = $daemon->getDaemonLog();
if ($daemon_log) {
$local_ids[] = $daemon_log->getID();
}
}
$daemon_query = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE);
if ($local_ids) {
$daemon_query->withoutIDs($local_ids);
}
$remote_daemons = $daemon_query->execute();
return array_merge($local_daemons, $remote_daemons);
}
private function findDaemonClass($substring) {
$symbols = $this->loadAvailableDaemonClasses();
@ -169,7 +124,7 @@ abstract class PhabricatorDaemonManagementWorkflow
$flags[] = '--verbose';
}
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
$instance = $this->getInstance();
if ($instance) {
$flags[] = '-l';
$flags[] = $instance;
@ -185,14 +140,6 @@ abstract class PhabricatorDaemonManagementWorkflow
$config['log'] = $this->getLogDirectory().'/daemons.log';
}
$pid_dir = $this->getPIDDirectory();
// TODO: This should be a much better user experience.
Filesystem::assertExists($pid_dir);
Filesystem::assertIsDirectory($pid_dir);
Filesystem::assertWritable($pid_dir);
$config['piddir'] = $pid_dir;
$config['daemons'] = $daemons;
$command = csprintf('./phd-daemon %Ls', $flags);
@ -324,28 +271,31 @@ abstract class PhabricatorDaemonManagementWorkflow
$console = PhutilConsole::getConsole();
if (!idx($options, 'force')) {
$running = $this->loadRunningDaemons();
$process_refs = $this->getOverseerProcessRefs();
if ($process_refs) {
$this->logWarn(
pht('RUNNING DAEMONS'),
pht('Daemons are already running:'));
// This may include daemons which were launched but which are no longer
// running; check that we actually have active daemons before failing.
foreach ($running as $daemon) {
if ($daemon->isRunning()) {
$message = pht(
"phd start: Unable to start daemons because daemons are already ".
"running.\n\n".
"You can view running daemons with '%s'.\n".
"You can stop running daemons with '%s'.\n".
"You can use '%s' to stop all daemons before starting ".
"new daemons.\n".
"You can force daemons to start anyway with %s.",
'phd status',
'phd stop',
'phd restart',
'--force');
$console->writeErr("%s\n", $message);
exit(1);
fprintf(STDERR, '%s', "\n");
foreach ($process_refs as $process_ref) {
fprintf(
STDERR,
'%s',
tsprintf(
" %s %s\n",
$process_ref->getPID(),
$process_ref->getCommand()));
}
fprintf(STDERR, '%s', "\n");
$this->logFail(
pht('RUNNING DAEMONS'),
pht(
'Use "phd stop" to stop daemons, "phd restart" to restart '.
'daemons, or "phd start --force" to ignore running processes.'));
exit(1);
}
}
@ -386,148 +336,115 @@ abstract class PhabricatorDaemonManagementWorkflow
return 0;
}
final protected function executeStopCommand(
array $pids,
array $options) {
$console = PhutilConsole::getConsole();
final protected function executeStopCommand(array $options) {
$grace_period = idx($options, 'graceful', 15);
$force = idx($options, 'force');
$gently = idx($options, 'gently');
if ($gently && $force) {
throw new PhutilArgumentUsageException(
$query = id(new PhutilProcessQuery())
->withIsOverseer(true);
$instance = $this->getInstance();
if ($instance !== null && !$force) {
$query->withInstances(array($instance));
}
try {
$process_refs = $query->execute();
} catch (Exception $ex) {
// See T13321. If this fails for some reason, just continue for now so
// that daemon management still works. In the long run, we don't expect
// this to fail, but I don't want to break this workflow while we iron
// bugs out.
// See T12827. Particularly, this is likely to fail on Solaris.
phlog($ex);
$process_refs = array();
}
if (!$process_refs) {
if ($instance !== null && !$force) {
$this->logInfo(
pht('NO DAEMONS'),
pht(
'There are no running daemons for the current instance ("%s"). '.
'Use "--force" to stop daemons for all instances.',
$instance));
} else {
$this->logInfo(
pht('NO DAEMONS'),
pht('There are no running daemons.'));
}
return 0;
}
$process_refs = mpull($process_refs, null, 'getPID');
$stop_pids = array_keys($process_refs);
$live_pids = $this->sendStopSignals($stop_pids, $grace_period);
$stop_pids = array_fuse($stop_pids);
$live_pids = array_fuse($live_pids);
$dead_pids = array_diff_key($stop_pids, $live_pids);
foreach ($dead_pids as $dead_pid) {
$dead_ref = $process_refs[$dead_pid];
$this->logOkay(
pht('STOP'),
pht(
'You can not specify conflicting options %s and %s together.',
'--gently',
'--force'));
'Stopped PID %d ("%s")',
$dead_pid,
$dead_ref->getCommand()));
}
$daemons = $this->loadRunningDaemons();
if (!$daemons) {
$survivors = array();
if (!$pids && !$gently) {
$survivors = $this->processRogueDaemons(
$grace_period,
$warn = true,
$force);
}
if (!$survivors) {
$console->writeErr(
"%s\n",
pht('There are no running Phabricator daemons.'));
}
return 0;
foreach ($live_pids as $live_pid) {
$live_ref = $process_refs[$live_pid];
$this->logFail(
pht('SURVIVED'),
pht(
'Unable to stop PID %d ("%s").',
$live_pid,
$live_ref->getCommand()));
}
$stop_pids = $this->selectDaemonPIDs($daemons, $pids);
if (!$stop_pids) {
$console->writeErr("%s\n", pht('No daemons to kill.'));
return 0;
}
$survivors = $this->sendStopSignals($stop_pids, $grace_period);
// Try to clean up PID files for daemons we killed.
$remove = array();
foreach ($daemons as $daemon) {
$pid = $daemon->getPID();
if (empty($stop_pids[$pid])) {
// We did not try to stop this overseer.
continue;
}
if (isset($survivors[$pid])) {
// We weren't able to stop this overseer.
continue;
}
if (!$daemon->getPIDFile()) {
// We don't know where the PID file is.
continue;
}
$remove[] = $daemon->getPIDFile();
}
foreach (array_unique($remove) as $remove_file) {
Filesystem::remove($remove_file);
}
if (!$gently) {
$this->processRogueDaemons($grace_period, !$pids, $force);
if ($live_pids) {
$this->logWarn(
pht('SURVIVORS'),
pht(
'Unable to stop all daemon processes. You may need to run this '.
'command as root with "sudo".'));
}
return 0;
}
final protected function executeReloadCommand(array $pids) {
$console = PhutilConsole::getConsole();
$process_refs = $this->getOverseerProcessRefs();
if (!$process_refs) {
$this->logInfo(
pht('NO DAEMONS'),
pht('There are no running daemon processes to reload.'));
$daemons = $this->loadRunningDaemons();
if (!$daemons) {
$console->writeErr(
"%s\n",
pht('There are no running daemons to reload.'));
return 0;
}
$reload_pids = $this->selectDaemonPIDs($daemons, $pids);
if (!$reload_pids) {
$console->writeErr(
"%s\n",
pht('No daemons to reload.'));
return 0;
}
foreach ($process_refs as $process_ref) {
$pid = $process_ref->getPID();
foreach ($reload_pids as $pid) {
$console->writeOut(
"%s\n",
$this->logInfo(
pht('RELOAD'),
pht('Reloading process %d...', $pid));
posix_kill($pid, SIGHUP);
}
return 0;
}
private function processRogueDaemons($grace_period, $warn, $force_stop) {
$console = PhutilConsole::getConsole();
$rogue_daemons = PhutilDaemonOverseer::findRunningDaemons();
if ($rogue_daemons) {
if ($force_stop) {
$rogue_pids = ipull($rogue_daemons, 'pid');
$survivors = $this->sendStopSignals($rogue_pids, $grace_period);
if ($survivors) {
$console->writeErr(
"%s\n",
pht(
'Unable to stop processes running without PID files. '.
'Try running this command again with sudo.'));
}
} else if ($warn) {
$console->writeErr("%s\n", $this->getForceStopHint($rogue_daemons));
}
}
return $rogue_daemons;
}
private function getForceStopHint($rogue_daemons) {
$debug_output = '';
foreach ($rogue_daemons as $rogue) {
$debug_output .= $rogue['pid'].' '.$rogue['command']."\n";
}
return pht(
"There are processes running that look like Phabricator daemons but ".
"have no corresponding PID files:\n\n%s\n\n".
"Stop these processes by re-running this command with the %s parameter.",
$debug_output,
'--force');
}
private function sendStopSignals($pids, $grace_period) {
// If we're doing a graceful shutdown, try SIGINT first.
if ($grace_period) {
@ -674,4 +591,21 @@ abstract class PhabricatorDaemonManagementWorkflow
return $select_pids;
}
protected function getOverseerProcessRefs() {
$query = id(new PhutilProcessQuery())
->withIsOverseer(true);
$instance = PhabricatorEnv::getEnvConfig('cluster.instance');
if ($instance !== null) {
$query->withInstances(array($instance));
}
return $query->execute();
}
protected function getInstance() {
return PhabricatorEnv::getEnvConfig('cluster.instance');
}
}

View file

@ -1375,16 +1375,16 @@ abstract class PhabricatorApplicationTransaction
public function getActionStrength() {
if ($this->isInlineCommentTransaction()) {
return 250;
return 25;
}
switch ($this->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
return 500;
return 50;
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
if ($this->isSelfSubscription()) {
// Make this weaker than TYPE_COMMENT.
return 250;
return 25;
}
if ($this->isApplicationAuthor()) {
@ -1396,14 +1396,14 @@ abstract class PhabricatorApplicationTransaction
// In other cases, subscriptions are more interesting than comments
// (which are shown anyway) but less interesting than any other type of
// transaction.
return 750;
return 75;
case PhabricatorTransactions::TYPE_MFA:
// We want MFA signatures to render at the top of transaction groups,
// on top of the things they signed.
return 10000;
return 1000;
}
return 1000;
return 100;
}
public function isCommentTransaction() {

View file

@ -1,128 +1,10 @@
<?php
// TODO: See T13321. After the removal of daemon PID files this class
// no longer makes as much sense as it once did.
final class PhabricatorDaemonReference extends Phobject {
private $name;
private $argv;
private $pid;
private $start;
private $pidFile;
private $daemonLog;
public static function loadReferencesFromFile($path) {
$pid_data = Filesystem::readFile($path);
try {
$dict = phutil_json_decode($pid_data);
} catch (PhutilJSONParserException $ex) {
$dict = array();
}
$refs = array();
$daemons = idx($dict, 'daemons', array());
$logs = array();
$daemon_ids = ipull($daemons, 'id');
if ($daemon_ids) {
try {
$logs = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withDaemonIDs($daemon_ids)
->execute();
} catch (AphrontQueryException $ex) {
// Ignore any issues here; getting this information only allows us
// to provide a more complete picture of daemon status, and we want
// these commands to work if the database is inaccessible.
}
$logs = mpull($logs, null, 'getDaemonID');
}
// Support PID files that use the old daemon format, where each overseer
// had exactly one daemon. We can eventually remove this; they will still
// be stopped by `phd stop --force` even if we don't identify them here.
if (!$daemons && idx($dict, 'name')) {
$daemons = array(
array(
'config' => array(
'class' => idx($dict, 'name'),
'argv' => idx($dict, 'argv', array()),
),
),
);
}
foreach ($daemons as $daemon) {
$ref = new PhabricatorDaemonReference();
// NOTE: This is the overseer PID, not the actual daemon process PID.
// This is correct for checking status and sending signals (the only
// things we do with it), but might be confusing. $daemon['pid'] has
// the daemon PID, and we could expose that if we had some use for it.
$ref->pid = idx($dict, 'pid');
$ref->start = idx($dict, 'start');
$config = idx($daemon, 'config', array());
$ref->name = idx($config, 'class');
$ref->argv = idx($config, 'argv', array());
$log = idx($logs, idx($daemon, 'id'));
if ($log) {
$ref->daemonLog = $log;
}
$ref->pidFile = $path;
$refs[] = $ref;
}
return $refs;
}
public function updateStatus($new_status) {
if (!$this->daemonLog) {
return;
}
try {
$this->daemonLog
->setStatus($new_status)
->save();
} catch (AphrontQueryException $ex) {
// Ignore anything that goes wrong here.
}
}
public function getPID() {
return $this->pid;
}
public function getName() {
return $this->name;
}
public function getArgv() {
return $this->argv;
}
public function getEpochStarted() {
return $this->start;
}
public function getPIDFile() {
return $this->pidFile;
}
public function getDaemonLog() {
return $this->daemonLog;
}
public function isRunning() {
return self::isProcessRunning($this->getPID());
}
public static function isProcessRunning($pid) {
if (!$pid) {
return false;
@ -148,15 +30,4 @@ final class PhabricatorDaemonReference extends Phobject {
return $is_running;
}
public function waitForExit($seconds) {
$start = time();
while (time() < $start + $seconds) {
usleep(100000);
if (!$this->isRunning()) {
return true;
}
}
return !$this->isRunning();
}
}

View file

@ -104,7 +104,11 @@ JX.install('Workflow', {
var link = event.getNode('tag:a');
// If the link is an anchor, or does not go anywhere, ignore the event.
var href = '' + link.getAttribute('href');
var href = link.getAttribute('href');
if (typeof href !== 'string') {
return;
}
if (!href.length || href[0] === '#') {
return;
}