1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 18:52:42 +01:00

(stable) Raise setup warnings immediately when failing to load configuration from the database

Summary:
Ref T11589. Previously, when we failed to load database configuration we just continued anyway, in order to get to setup checks so we could raise a better error.

There was a small chance that this could lead to pages running in a broken state, where ONLY that connection failed and everything else worked. This was accidentally fixed by narrowing the exceptions we continue on in D16489.

However, this "fix" meant that users no longer got helpful setup instructions. Instead:

  - Keep throwing these exceptions: it's bad to continue if we've failed to connect to the database.
  - However, catch them and turn them into setup errors.
  - Share all the setup code so these errors and setup check errors work the same way.

Test Plan:
  - Intentionally broke `mysql.host` and `mysql.pass`.
  - Loaded pages.
  - Got good setup errors.
  - Hit normal setup errors too.
  - Put everything back.
  - Swapped into cluster mode.
  - Intentionally broke cluster mode, saw failover to readonly.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11589

Differential Revision: https://secure.phabricator.com/D16501
This commit is contained in:
epriestley 2016-09-06 12:52:03 -07:00
parent 445924a2a2
commit a1338bd2d8
4 changed files with 80 additions and 46 deletions

View file

@ -73,13 +73,26 @@ abstract class AphrontApplicationConfiguration extends Phobject {
$response = PhabricatorSetupCheck::willPreflightRequest(); $response = PhabricatorSetupCheck::willPreflightRequest();
if ($response) { if ($response) {
PhabricatorStartup::endOutputCapture(); return self::writeResponse($sink, $response);
$sink->writeResponse($response);
return;
} }
PhabricatorStartup::beginStartupPhase('env.init'); PhabricatorStartup::beginStartupPhase('env.init');
PhabricatorEnv::initializeWebEnvironment();
try {
PhabricatorEnv::initializeWebEnvironment();
$database_exception = null;
} catch (AphrontInvalidCredentialsQueryException $ex) {
$database_exception = $ex;
} catch (AphrontConnectionQueryException $ex) {
$database_exception = $ex;
}
if ($database_exception) {
$issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
$database_exception);
$response = PhabricatorSetupCheck::newIssueResponse($issue);
return self::writeResponse($sink, $response);
}
$multimeter->setSampleRate( $multimeter->setSampleRate(
PhabricatorEnv::getEnvConfig('debug.sample-rate')); PhabricatorEnv::getEnvConfig('debug.sample-rate'));
@ -111,9 +124,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
$response = PhabricatorSetupCheck::willProcessRequest(); $response = PhabricatorSetupCheck::willProcessRequest();
if ($response) { if ($response) {
PhabricatorStartup::endOutputCapture(); return self::writeResponse($sink, $response);
$sink->writeResponse($response);
return;
} }
$host = AphrontRequest::getHTTPHeader('Host'); $host = AphrontRequest::getHTTPHeader('Host');
@ -256,31 +267,7 @@ abstract class AphrontApplicationConfiguration extends Phobject {
$response = $controller->willSendResponse($response); $response = $controller->willSendResponse($response);
$response->setRequest($request); $response->setRequest($request);
$unexpected_output = PhabricatorStartup::endOutputCapture(); self::writeResponse($sink, $response);
if ($unexpected_output) {
$unexpected_output = pht(
"Unexpected output:\n\n%s",
$unexpected_output);
phlog($unexpected_output);
if ($response instanceof AphrontWebpageResponse) {
echo phutil_tag(
'div',
array(
'style' =>
'background: #eeddff;'.
'white-space: pre-wrap;'.
'z-index: 200000;'.
'position: relative;'.
'padding: 8px;'.
'font-family: monospace',
),
$unexpected_output);
}
}
$sink->writeResponse($response);
} catch (Exception $ex) { } catch (Exception $ex) {
if ($original_exception) { if ($original_exception) {
throw $original_exception; throw $original_exception;
@ -291,6 +278,37 @@ abstract class AphrontApplicationConfiguration extends Phobject {
return $response; return $response;
} }
private static function writeResponse(
AphrontHTTPSink $sink,
AphrontResponse $response) {
$unexpected_output = PhabricatorStartup::endOutputCapture();
if ($unexpected_output) {
$unexpected_output = pht(
"Unexpected output:\n\n%s",
$unexpected_output);
phlog($unexpected_output);
if ($response instanceof AphrontWebpageResponse) {
echo phutil_tag(
'div',
array(
'style' =>
'background: #eeddff;'.
'white-space: pre-wrap;'.
'z-index: 200000;'.
'position: relative;'.
'padding: 8px;'.
'font-family: monospace',
),
$unexpected_output);
}
}
$sink->writeResponse($response);
}
/* -( URI Routing )-------------------------------------------------------- */ /* -( URI Routing )-------------------------------------------------------- */

View file

@ -23,21 +23,17 @@ final class PhabricatorDatabaseSetupCheck extends PhabricatorSetupCheck {
try { try {
queryfx($conn_raw, 'SELECT 1'); queryfx($conn_raw, 'SELECT 1');
$database_exception = null;
} catch (AphrontInvalidCredentialsQueryException $ex) {
$database_exception = $ex;
} catch (AphrontConnectionQueryException $ex) { } catch (AphrontConnectionQueryException $ex) {
$message = pht( $database_exception = $ex;
"Unable to connect to MySQL!\n\n". }
"%s\n\n".
"Make sure Phabricator and MySQL are correctly configured.",
$ex->getMessage());
$this->newIssue('mysql.connect') if ($database_exception) {
->setName(pht('Can Not Connect to MySQL')) $issue = PhabricatorSetupIssue::newDatabaseConnectionIssue(
->setMessage($message) $database_exception);
->setIsFatal(true) $this->addIssue($issue);
->addRelatedPhabricatorConfig('mysql.host')
->addRelatedPhabricatorConfig('mysql.port')
->addRelatedPhabricatorConfig('mysql.user')
->addRelatedPhabricatorConfig('mysql.pass');
return; return;
} }

View file

@ -147,7 +147,7 @@ abstract class PhabricatorSetupCheck extends Phobject {
return null; return null;
} }
private static function newIssueResponse(PhabricatorSetupIssue $issue) { public static function newIssueResponse(PhabricatorSetupIssue $issue) {
$view = id(new PhabricatorSetupIssueView()) $view = id(new PhabricatorSetupIssueView())
->setIssue($issue); ->setIssue($issue);

View file

@ -20,6 +20,26 @@ final class PhabricatorSetupIssue extends Phobject {
private $originalPHPConfigValues = array(); private $originalPHPConfigValues = array();
private $links; private $links;
public static function newDatabaseConnectionIssue(
AphrontQueryException $ex) {
$message = pht(
"Unable to connect to MySQL!\n\n".
"%s\n\n".
"Make sure Phabricator and MySQL are correctly configured.",
$ex->getMessage());
return id(new self())
->setIssueKey('mysql.connect')
->setName(pht('Can Not Connect to MySQL'))
->setMessage($message)
->setIsFatal(true)
->addRelatedPhabricatorConfig('mysql.host')
->addRelatedPhabricatorConfig('mysql.port')
->addRelatedPhabricatorConfig('mysql.user')
->addRelatedPhabricatorConfig('mysql.pass');
}
public function addCommand($command) { public function addCommand($command) {
$this->commands[] = $command; $this->commands[] = $command;
return $this; return $this;