@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+80 -46
+50 -32
src/aphront/configuration/AphrontApplicationConfiguration.php
··· 73 73 74 74 $response = PhabricatorSetupCheck::willPreflightRequest(); 75 75 if ($response) { 76 - PhabricatorStartup::endOutputCapture(); 77 - $sink->writeResponse($response); 78 - return; 76 + return self::writeResponse($sink, $response); 79 77 } 80 78 81 79 PhabricatorStartup::beginStartupPhase('env.init'); 82 - PhabricatorEnv::initializeWebEnvironment(); 80 + 81 + try { 82 + PhabricatorEnv::initializeWebEnvironment(); 83 + $database_exception = null; 84 + } catch (AphrontInvalidCredentialsQueryException $ex) { 85 + $database_exception = $ex; 86 + } catch (AphrontConnectionQueryException $ex) { 87 + $database_exception = $ex; 88 + } 89 + 90 + if ($database_exception) { 91 + $issue = PhabricatorSetupIssue::newDatabaseConnectionIssue( 92 + $database_exception); 93 + $response = PhabricatorSetupCheck::newIssueResponse($issue); 94 + return self::writeResponse($sink, $response); 95 + } 83 96 84 97 $multimeter->setSampleRate( 85 98 PhabricatorEnv::getEnvConfig('debug.sample-rate')); ··· 111 124 112 125 $response = PhabricatorSetupCheck::willProcessRequest(); 113 126 if ($response) { 114 - PhabricatorStartup::endOutputCapture(); 115 - $sink->writeResponse($response); 116 - return; 127 + return self::writeResponse($sink, $response); 117 128 } 118 129 119 130 $host = AphrontRequest::getHTTPHeader('Host'); ··· 256 267 $response = $controller->willSendResponse($response); 257 268 $response->setRequest($request); 258 269 259 - $unexpected_output = PhabricatorStartup::endOutputCapture(); 260 - if ($unexpected_output) { 261 - $unexpected_output = pht( 262 - "Unexpected output:\n\n%s", 263 - $unexpected_output); 264 - 265 - phlog($unexpected_output); 266 - 267 - if ($response instanceof AphrontWebpageResponse) { 268 - echo phutil_tag( 269 - 'div', 270 - array( 271 - 'style' => 272 - 'background: #eeddff;'. 273 - 'white-space: pre-wrap;'. 274 - 'z-index: 200000;'. 275 - 'position: relative;'. 276 - 'padding: 8px;'. 277 - 'font-family: monospace', 278 - ), 279 - $unexpected_output); 280 - } 281 - } 282 - 283 - $sink->writeResponse($response); 270 + self::writeResponse($sink, $response); 284 271 } catch (Exception $ex) { 285 272 if ($original_exception) { 286 273 throw $original_exception; ··· 289 276 } 290 277 291 278 return $response; 279 + } 280 + 281 + private static function writeResponse( 282 + AphrontHTTPSink $sink, 283 + AphrontResponse $response) { 284 + 285 + $unexpected_output = PhabricatorStartup::endOutputCapture(); 286 + if ($unexpected_output) { 287 + $unexpected_output = pht( 288 + "Unexpected output:\n\n%s", 289 + $unexpected_output); 290 + 291 + phlog($unexpected_output); 292 + 293 + if ($response instanceof AphrontWebpageResponse) { 294 + echo phutil_tag( 295 + 'div', 296 + array( 297 + 'style' => 298 + 'background: #eeddff;'. 299 + 'white-space: pre-wrap;'. 300 + 'z-index: 200000;'. 301 + 'position: relative;'. 302 + 'padding: 8px;'. 303 + 'font-family: monospace', 304 + ), 305 + $unexpected_output); 306 + } 307 + } 308 + 309 + $sink->writeResponse($response); 292 310 } 293 311 294 312
+9 -13
src/applications/config/check/PhabricatorDatabaseSetupCheck.php
··· 23 23 24 24 try { 25 25 queryfx($conn_raw, 'SELECT 1'); 26 + $database_exception = null; 27 + } catch (AphrontInvalidCredentialsQueryException $ex) { 28 + $database_exception = $ex; 26 29 } catch (AphrontConnectionQueryException $ex) { 27 - $message = pht( 28 - "Unable to connect to MySQL!\n\n". 29 - "%s\n\n". 30 - "Make sure Phabricator and MySQL are correctly configured.", 31 - $ex->getMessage()); 30 + $database_exception = $ex; 31 + } 32 32 33 - $this->newIssue('mysql.connect') 34 - ->setName(pht('Can Not Connect to MySQL')) 35 - ->setMessage($message) 36 - ->setIsFatal(true) 37 - ->addRelatedPhabricatorConfig('mysql.host') 38 - ->addRelatedPhabricatorConfig('mysql.port') 39 - ->addRelatedPhabricatorConfig('mysql.user') 40 - ->addRelatedPhabricatorConfig('mysql.pass'); 33 + if ($database_exception) { 34 + $issue = PhabricatorSetupIssue::newDatabaseConnectionIssue( 35 + $database_exception); 36 + $this->addIssue($issue); 41 37 return; 42 38 } 43 39
+1 -1
src/applications/config/check/PhabricatorSetupCheck.php
··· 147 147 return null; 148 148 } 149 149 150 - private static function newIssueResponse(PhabricatorSetupIssue $issue) { 150 + public static function newIssueResponse(PhabricatorSetupIssue $issue) { 151 151 $view = id(new PhabricatorSetupIssueView()) 152 152 ->setIssue($issue); 153 153
+20
src/applications/config/issue/PhabricatorSetupIssue.php
··· 20 20 private $originalPHPConfigValues = array(); 21 21 private $links; 22 22 23 + public static function newDatabaseConnectionIssue( 24 + AphrontQueryException $ex) { 25 + 26 + $message = pht( 27 + "Unable to connect to MySQL!\n\n". 28 + "%s\n\n". 29 + "Make sure Phabricator and MySQL are correctly configured.", 30 + $ex->getMessage()); 31 + 32 + return id(new self()) 33 + ->setIssueKey('mysql.connect') 34 + ->setName(pht('Can Not Connect to MySQL')) 35 + ->setMessage($message) 36 + ->setIsFatal(true) 37 + ->addRelatedPhabricatorConfig('mysql.host') 38 + ->addRelatedPhabricatorConfig('mysql.port') 39 + ->addRelatedPhabricatorConfig('mysql.user') 40 + ->addRelatedPhabricatorConfig('mysql.pass'); 41 + } 42 + 23 43 public function addCommand($command) { 24 44 $this->commands[] = $command; 25 45 return $this;