@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.

Update `bin/storage` workflows to accommodate multiple masters

Summary: Depends on D16847. Ref T11044. This updates the remaining storage-related workflows from the CLI to accommodate multiple masters.

Test Plan:
- Configured multiple masters.
- Ran all `bin/storage` workflows.
- Ran `arc unit --everything`.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11044

Differential Revision: https://secure.phabricator.com/D16848

+375 -203
+129 -96
scripts/sql/manage_storage.php
··· 34 34 'name' => 'host', 35 35 'param' => 'hostname', 36 36 'help' => pht( 37 - 'Connect to __host__ instead of the default host.'), 37 + 'Operate on the database server identified by __hostname__.'), 38 + ), 39 + array( 40 + 'name' => 'ref', 41 + 'param' => 'ref', 42 + 'help' => pht( 43 + 'Operate on the database identified by __ref__.'), 38 44 ), 39 45 array( 40 46 'name' => 'user', ··· 81 87 // First, test that the Phabricator configuration is set up correctly. After 82 88 // we know this works we'll test any administrative credentials specifically. 83 89 90 + $refs = PhabricatorDatabaseRef::getActiveDatabaseRefs(); 91 + if (!$refs) { 92 + throw new PhutilArgumentUsageException( 93 + pht('No databases are configured.')); 94 + } 95 + 84 96 $host = $args->getArg('host'); 85 - if (strlen($host)) { 86 - $ref = null; 97 + $ref_key = $args->getArg('ref'); 98 + if (strlen($host) || strlen($ref_key)) { 99 + if ($host && $ref_key) { 100 + throw new PhutilArgumentUsageException( 101 + pht( 102 + 'Use "--host" or "--ref" to select a database, but not both.')); 103 + } 87 104 88 - $refs = PhabricatorDatabaseRef::getLiveRefs(); 105 + $refs = PhabricatorDatabaseRef::getActiveDatabaseRefs(); 89 106 90 - // Include the master in case the user is just specifying a redundant 91 - // "--host" flag for no reason and does not actually have a database 92 - // cluster configured. 93 - foreach (PhabricatorDatabaseRef::getMasterDatabaseRefs() as $master_ref) { 94 - $refs[] = $master_ref; 107 + $possible_refs = array(); 108 + foreach ($refs as $possible_ref) { 109 + if ($host && ($possible_ref->getHost() == $host)) { 110 + $possible_refs[] = $possible_ref; 111 + break; 112 + } 113 + if ($ref_key && ($possible_ref->getRefKey() == $ref_key)) { 114 + $possible_refs[] = $possible_ref; 115 + break; 116 + } 95 117 } 96 118 97 - foreach ($refs as $possible_ref) { 98 - if ($possible_ref->getHost() == $host) { 99 - $ref = $possible_ref; 100 - break; 119 + if (!$possible_refs) { 120 + if ($host) { 121 + throw new PhutilArgumentUsageException( 122 + pht( 123 + 'There is no configured database on host "%s". This command can '. 124 + 'only interact with configured databases.', 125 + $host)); 126 + } else { 127 + throw new PhutilArgumentUsageException( 128 + pht( 129 + 'There is no configured database with ref "%s". This command can '. 130 + 'only interact with configured databases.', 131 + $ref_key)); 101 132 } 102 133 } 103 134 104 - if (!$ref) { 135 + if (count($possible_refs) > 1) { 105 136 throw new PhutilArgumentUsageException( 106 137 pht( 107 - 'There is no configured database on host "%s". This command can '. 108 - 'only interact with configured databases.', 138 + 'Host "%s" identifies more than one database. Use "--ref" to select '. 139 + 'a specific database.', 109 140 $host)); 110 141 } 111 - } else { 112 - $ref = PhabricatorDatabaseRef::getMasterDatabaseRef(); 113 - if (!$ref) { 114 - throw new Exception( 115 - pht('No database master is configured.')); 116 - } 142 + 143 + $refs = $possible_refs; 117 144 } 118 145 119 - $default_user = $ref->getUser(); 120 - $default_host = $ref->getHost(); 121 - $default_port = $ref->getPort(); 146 + $apis = array(); 147 + foreach ($refs as $ref) { 148 + $default_user = $ref->getUser(); 149 + $default_host = $ref->getHost(); 150 + $default_port = $ref->getPort(); 122 151 123 - $test_api = id(new PhabricatorStorageManagementAPI()) 124 - ->setUser($default_user) 125 - ->setHost($default_host) 126 - ->setPort($default_port) 127 - ->setPassword($ref->getPass()) 128 - ->setNamespace($args->getArg('namespace')); 152 + $test_api = id(new PhabricatorStorageManagementAPI()) 153 + ->setUser($default_user) 154 + ->setHost($default_host) 155 + ->setPort($default_port) 156 + ->setPassword($ref->getPass()) 157 + ->setNamespace($args->getArg('namespace')); 158 + 159 + try { 160 + queryfx( 161 + $test_api->getConn(null), 162 + 'SELECT 1'); 163 + } catch (AphrontQueryException $ex) { 164 + $message = phutil_console_format( 165 + "**%s**\n\n%s\n\n%s\n\n%s\n\n**%s**: %s\n", 166 + pht('MySQL Credentials Not Configured'), 167 + pht( 168 + 'Unable to connect to MySQL using the configured credentials. '. 169 + 'You must configure standard credentials before you can upgrade '. 170 + 'storage. Run these commands to set up credentials:'), 171 + " phabricator/ $ ./bin/config set mysql.host __host__\n". 172 + " phabricator/ $ ./bin/config set mysql.user __username__\n". 173 + " phabricator/ $ ./bin/config set mysql.pass __password__", 174 + pht( 175 + 'These standard credentials are separate from any administrative '. 176 + 'credentials provided to this command with __%s__ or '. 177 + '__%s__, and must be configured correctly before you can proceed.', 178 + '--user', 179 + '--password'), 180 + pht('Raw MySQL Error'), 181 + $ex->getMessage()); 182 + echo phutil_console_wrap($message); 183 + exit(1); 184 + } 129 185 130 - try { 131 - queryfx( 132 - $test_api->getConn(null), 133 - 'SELECT 1'); 134 - } catch (AphrontQueryException $ex) { 135 - $message = phutil_console_format( 136 - "**%s**\n\n%s\n\n%s\n\n%s\n\n**%s**: %s\n", 137 - pht('MySQL Credentials Not Configured'), 138 - pht( 139 - 'Unable to connect to MySQL using the configured credentials. '. 140 - 'You must configure standard credentials before you can upgrade '. 141 - 'storage. Run these commands to set up credentials:'), 142 - " phabricator/ $ ./bin/config set mysql.host __host__\n". 143 - " phabricator/ $ ./bin/config set mysql.user __username__\n". 144 - " phabricator/ $ ./bin/config set mysql.pass __password__", 145 - pht( 146 - 'These standard credentials are separate from any administrative '. 147 - 'credentials provided to this command with __%s__ or '. 148 - '__%s__, and must be configured correctly before you can proceed.', 149 - '--user', 150 - '--password'), 151 - pht('Raw MySQL Error'), 152 - $ex->getMessage()); 153 - echo phutil_console_wrap($message); 154 - exit(1); 155 - } 186 + if ($args->getArg('password') === null) { 187 + // This is already a PhutilOpaqueEnvelope. 188 + $password = $ref->getPass(); 189 + } else { 190 + // Put this in a PhutilOpaqueEnvelope. 191 + $password = new PhutilOpaqueEnvelope($args->getArg('password')); 192 + PhabricatorEnv::overrideConfig('mysql.pass', $args->getArg('password')); 193 + } 156 194 157 - if ($args->getArg('password') === null) { 158 - // This is already a PhutilOpaqueEnvelope. 159 - $password = $ref->getPass(); 160 - } else { 161 - // Put this in a PhutilOpaqueEnvelope. 162 - $password = new PhutilOpaqueEnvelope($args->getArg('password')); 163 - PhabricatorEnv::overrideConfig('mysql.pass', $args->getArg('password')); 164 - } 195 + $selected_user = $args->getArg('user'); 196 + if ($selected_user === null) { 197 + $selected_user = $default_user; 198 + } 165 199 166 - $selected_user = $args->getArg('user'); 167 - if ($selected_user === null) { 168 - $selected_user = $default_user; 169 - } 200 + $api = id(new PhabricatorStorageManagementAPI()) 201 + ->setUser($selected_user) 202 + ->setHost($default_host) 203 + ->setPort($default_port) 204 + ->setPassword($password) 205 + ->setNamespace($args->getArg('namespace')) 206 + ->setDisableUTF8MB4($args->getArg('disable-utf8mb4')); 207 + PhabricatorEnv::overrideConfig('mysql.user', $api->getUser()); 170 208 171 - $api = id(new PhabricatorStorageManagementAPI()) 172 - ->setUser($selected_user) 173 - ->setHost($default_host) 174 - ->setPort($default_port) 175 - ->setPassword($password) 176 - ->setNamespace($args->getArg('namespace')) 177 - ->setDisableUTF8MB4($args->getArg('disable-utf8mb4')); 178 - PhabricatorEnv::overrideConfig('mysql.user', $api->getUser()); 209 + try { 210 + queryfx( 211 + $api->getConn(null), 212 + 'SELECT 1'); 213 + } catch (AphrontQueryException $ex) { 214 + $message = phutil_console_format( 215 + "**%s**\n\n%s\n\n**%s**: %s\n", 216 + pht('Bad Administrative Credentials'), 217 + pht( 218 + 'Unable to connect to MySQL using the administrative credentials '. 219 + 'provided with the __%s__ and __%s__ flags. Check that '. 220 + 'you have entered them correctly.', 221 + '--user', 222 + '--password'), 223 + pht('Raw MySQL Error'), 224 + $ex->getMessage()); 225 + echo phutil_console_wrap($message); 226 + exit(1); 227 + } 179 228 180 - try { 181 - queryfx( 182 - $api->getConn(null), 183 - 'SELECT 1'); 184 - } catch (AphrontQueryException $ex) { 185 - $message = phutil_console_format( 186 - "**%s**\n\n%s\n\n**%s**: %s\n", 187 - pht('Bad Administrative Credentials'), 188 - pht( 189 - 'Unable to connect to MySQL using the administrative credentials '. 190 - 'provided with the __%s__ and __%s__ flags. Check that '. 191 - 'you have entered them correctly.', 192 - '--user', 193 - '--password'), 194 - pht('Raw MySQL Error'), 195 - $ex->getMessage()); 196 - echo phutil_console_wrap($message); 197 - exit(1); 229 + $api->setRef($ref); 230 + $apis[] = $api; 198 231 } 199 232 200 233 $workflows = id(new PhutilClassMapQuery()) ··· 204 237 $patches = PhabricatorSQLPatchList::buildAllPatches(); 205 238 206 239 foreach ($workflows as $workflow) { 207 - $workflow->setAPI($api); 240 + $workflow->setAPIs($apis); 208 241 $workflow->setPatches($patches); 209 242 } 210 243
+32 -2
src/applications/config/schema/PhabricatorConfigSchemaQuery.php
··· 2 2 3 3 final class PhabricatorConfigSchemaQuery extends Phobject { 4 4 5 + private $refs; 6 + private $apis; 7 + 8 + public function setRefs(array $refs) { 9 + $this->refs = $refs; 10 + return $this; 11 + } 12 + 13 + public function getRefs() { 14 + if (!$this->refs) { 15 + return PhabricatorDatabaseRef::getMasterDatabaseRefs(); 16 + } 17 + return $this->refs; 18 + } 19 + 20 + public function setAPIs(array $apis) { 21 + $map = array(); 22 + foreach ($apis as $api) { 23 + $map[$api->getRef()->getRefKey()] = $api; 24 + } 25 + $this->apis = $map; 26 + return $this; 27 + } 28 + 5 29 private function getDatabaseNames(PhabricatorDatabaseRef $ref) { 6 30 $api = $this->getAPI($ref); 7 31 $patches = PhabricatorSQLPatchList::buildAllPatches(); ··· 11 35 } 12 36 13 37 private function getAPI(PhabricatorDatabaseRef $ref) { 38 + $key = $ref->getRefKey(); 39 + 40 + if (isset($this->apis[$key])) { 41 + return $this->apis[$key]; 42 + } 43 + 14 44 return id(new PhabricatorStorageManagementAPI()) 15 45 ->setUser($ref->getUser()) 16 46 ->setHost($ref->getHost()) ··· 20 50 } 21 51 22 52 public function loadActualSchemata() { 23 - $refs = PhabricatorDatabaseRef::getMasterDatabaseRefs(); 53 + $refs = $this->getRefs(); 24 54 25 55 $schemata = array(); 26 56 foreach ($refs as $ref) { ··· 183 213 } 184 214 185 215 public function loadExpectedSchemata() { 186 - $refs = PhabricatorDatabaseRef::getMasterDatabaseRefs(); 216 + $refs = $this->getRefs(); 187 217 188 218 $schemata = array(); 189 219 foreach ($refs as $ref) {
+17 -10
src/infrastructure/cluster/PhabricatorDatabaseRef.php
··· 223 223 ); 224 224 } 225 225 226 - public static function getLiveRefs() { 226 + public static function getClusterRefs() { 227 227 $cache = PhabricatorCaches::getRequestCache(); 228 228 229 229 $refs = $cache->getKey(self::KEY_REFS); ··· 457 457 return $this->healthRecord; 458 458 } 459 459 460 + public static function getActiveDatabaseRefs() { 461 + $refs = array(); 462 + 463 + foreach (self::getMasterDatabaseRefs() as $ref) { 464 + $refs[] = $ref; 465 + } 466 + 467 + foreach (self::getReplicaDatabaseRefs() as $ref) { 468 + $refs[] = $ref; 469 + } 470 + 471 + return $refs; 472 + } 473 + 460 474 public static function getMasterDatabaseRefs() { 461 - $refs = self::getLiveRefs(); 475 + $refs = self::getClusterRefs(); 462 476 463 477 if (!$refs) { 464 478 return array(self::getLiveIndividualRef()); ··· 477 491 return $masters; 478 492 } 479 493 480 - public static function getMasterDatabaseRef() { 481 - // TODO: Remove this method; it no longer makes sense with application 482 - // partitioning. 483 - 484 - return head(self::getMasterDatabaseRefs()); 485 - } 486 - 487 494 public static function getMasterDatabaseRefForDatabase($database) { 488 495 $masters = self::getMasterDatabaseRefs(); 489 496 ··· 507 514 } 508 515 509 516 public static function getReplicaDatabaseRefs() { 510 - $refs = self::getLiveRefs(); 517 + $refs = self::getClusterRefs(); 511 518 512 519 if (!$refs) { 513 520 return array();
+10
src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php
··· 2 2 3 3 final class PhabricatorStorageManagementAPI extends Phobject { 4 4 5 + private $ref; 5 6 private $host; 6 7 private $user; 7 8 private $port; ··· 72 73 73 74 public function getPort() { 74 75 return $this->port; 76 + } 77 + 78 + public function setRef(PhabricatorDatabaseRef $ref) { 79 + $this->ref = $ref; 80 + return $this; 81 + } 82 + 83 + public function getRef() { 84 + return $this->ref; 75 85 } 76 86 77 87 public function getDatabaseName($fragment) {
+11 -4
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementAdjustWorkflow.php
··· 27 27 public function didExecute(PhutilArgumentParser $args) { 28 28 $unsafe = $args->getArg('unsafe'); 29 29 30 - $this->requireAllPatchesApplied(); 31 - return $this->adjustSchemata($unsafe); 30 + foreach ($this->getMasterAPIs() as $api) { 31 + $this->requireAllPatchesApplied($api); 32 + $err = $this->adjustSchemata($api, $unsafe); 33 + if ($err) { 34 + return $err; 35 + } 36 + } 37 + 38 + return 0; 32 39 } 33 40 34 - private function requireAllPatchesApplied() { 35 - $api = $this->getAPI(); 41 + private function requireAllPatchesApplied( 42 + PhabricatorStorageManagementAPI $api) { 36 43 $applied = $api->getAppliedPatches(); 37 44 38 45 if ($applied === null) {
+2 -1
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDatabasesWorkflow.php
··· 15 15 } 16 16 17 17 public function didExecute(PhutilArgumentParser $args) { 18 - $api = $this->getAPI(); 18 + $api = $this->getAnyAPI(); 19 + 19 20 $patches = $this->getPatches(); 20 21 21 22 $databases = $api->getDatabaseList($patches, true);
+2 -1
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDestroyWorkflow.php
··· 23 23 public function didExecute(PhutilArgumentParser $args) { 24 24 $console = PhutilConsole::getConsole(); 25 25 26 + $api = $this->getSingleAPI(); 27 + 26 28 if (!$this->isDryRun() && !$this->isForce()) { 27 29 $console->writeOut( 28 30 phutil_console_wrap( ··· 42 44 } 43 45 } 44 46 45 - $api = $this->getAPI(); 46 47 $patches = $this->getPatches(); 47 48 48 49 if ($args->getArg('unittest-fixtures')) {
+1 -1
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementDumpWorkflow.php
··· 44 44 } 45 45 46 46 public function didExecute(PhutilArgumentParser $args) { 47 - $api = $this->getAPI(); 47 + $api = $this->getSingleAPI(); 48 48 $patches = $this->getPatches(); 49 49 50 50 $console = PhutilConsole::getConsole();
+3 -2
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementProbeWorkflow.php
··· 15 15 } 16 16 17 17 public function didExecute(PhutilArgumentParser $args) { 18 + $api = $this->getSingleAPI(); 19 + 18 20 $console = PhutilConsole::getConsole(); 19 21 $console->writeErr( 20 22 "%s\n", 21 23 pht('Analyzing table sizes (this may take a moment)...')); 22 24 23 - $api = $this->getAPI(); 24 - $patches = $this->getPatches(); 25 + $patches = $this->getPatches(); 25 26 $databases = $api->getDatabaseList($patches, true); 26 27 27 28 $conn_r = $api->getConn(null);
+19 -8
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementQuickstartWorkflow.php
··· 36 36 37 37 $bin = dirname(phutil_get_library_root('phabricator')).'/bin/storage'; 38 38 39 - if (!$this->getAPI()->isCharacterSetAvailable('utf8mb4')) { 39 + // We don't care which database we're using to generate a quickstart file, 40 + // since all of the schemata should be identical. 41 + $api = $this->getAnyAPI(); 42 + 43 + $ref = $api->getRef(); 44 + $ref_key = $ref->getRefKey(); 45 + 46 + if (!$api->isCharacterSetAvailable('utf8mb4')) { 40 47 throw new PhutilArgumentUsageException( 41 48 pht( 42 49 'You can only generate a new quickstart file if MySQL supports '. ··· 47 54 } 48 55 49 56 $err = phutil_passthru( 50 - '%s upgrade --force --no-quickstart --namespace %s', 57 + '%s upgrade --force --no-quickstart --namespace %s --ref %s', 51 58 $bin, 52 - $namespace); 59 + $namespace, 60 + $ref_key); 53 61 if ($err) { 54 62 return $err; 55 63 } 56 64 57 65 $err = phutil_passthru( 58 - '%s adjust --force --namespace %s', 66 + '%s adjust --force --namespace %s --ref %s', 59 67 $bin, 60 - $namespace); 68 + $namespace, 69 + $ref_key); 61 70 if ($err) { 62 71 return $err; 63 72 } 64 73 65 74 $tmp = new TempFile(); 66 75 $err = phutil_passthru( 67 - '%s dump --namespace %s > %s', 76 + '%s dump --namespace %s --ref %s > %s', 68 77 $bin, 69 78 $namespace, 79 + $ref_key, 70 80 $tmp); 71 81 if ($err) { 72 82 return $err; 73 83 } 74 84 75 85 $err = phutil_passthru( 76 - '%s destroy --force --namespace %s', 86 + '%s destroy --force --namespace %s --ref %s', 77 87 $bin, 78 - $namespace); 88 + $namespace, 89 + $ref_key); 79 90 if ($err) { 80 91 return $err; 81 92 }
+7 -3
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementRenamespaceWorkflow.php
··· 62 62 if (!strlen($input) && !$is_live) { 63 63 throw new PhutilArgumentUsageException( 64 64 pht( 65 - 'Specify the dumpfile to read with "--in", or use "--live" to '. 65 + 'Specify the dumpfile to read with "--input", or use "--live" to '. 66 66 'generate one automatically.')); 67 67 } 68 68 ··· 108 108 } 109 109 110 110 if ($is_live) { 111 + $api = $this->getSingleAPI(); 112 + $ref_key = $api->getRef()->getRefKey(); 113 + 111 114 $root = dirname(phutil_get_library_root('phabricator')); 112 115 113 116 $future = new ExecFuture( 114 - '%R dump', 115 - $root.'/bin/storage'); 117 + '%R dump --ref %s', 118 + $root.'/bin/storage', 119 + $ref_key); 116 120 117 121 $lines = new LinesOfALargeExecFuture($future); 118 122 } else {
+1 -1
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementShellWorkflow.php
··· 15 15 } 16 16 17 17 public function execute(PhutilArgumentParser $args) { 18 - $api = $this->getAPI(); 18 + $api = $this->getSingleAPI(); 19 19 list($host, $port) = $this->getBareHostAndPort($api->getHost()); 20 20 21 21 $flag_port = $port
+39 -35
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementStatusWorkflow.php
··· 15 15 } 16 16 17 17 public function didExecute(PhutilArgumentParser $args) { 18 - $api = $this->getAPI(); 19 - $patches = $this->getPatches(); 18 + foreach ($this->getAPIs() as $api) { 19 + $patches = $this->getPatches(); 20 + $applied = $api->getAppliedPatches(); 20 21 21 - $applied = $api->getAppliedPatches(); 22 + if ($applied === null) { 23 + echo phutil_console_format( 24 + "**%s**: %s\n", 25 + pht('Database Not Initialized'), 26 + pht('Run **%s** to initialize.', './bin/storage upgrade')); 22 27 23 - if ($applied === null) { 24 - echo phutil_console_format( 25 - "**%s**: %s\n", 26 - pht('Database Not Initialized'), 27 - pht('Run **%s** to initialize.', './bin/storage upgrade')); 28 + return 1; 29 + } 28 30 29 - return 1; 30 - } 31 + $ref = $api->getRef(); 31 32 32 - $table = id(new PhutilConsoleTable()) 33 - ->setShowHeader(false) 34 - ->addColumn('id', array('title' => pht('ID'))) 35 - ->addColumn('status', array('title' => pht('Status'))) 36 - ->addColumn('duration', array('title' => pht('Duration'))) 37 - ->addColumn('type', array('title' => pht('Type'))) 38 - ->addColumn('name', array('title' => pht('Name'))); 33 + $table = id(new PhutilConsoleTable()) 34 + ->setShowHeader(false) 35 + ->addColumn('id', array('title' => pht('ID'))) 36 + ->addColumn('host', array('title' => pht('Host'))) 37 + ->addColumn('status', array('title' => pht('Status'))) 38 + ->addColumn('duration', array('title' => pht('Duration'))) 39 + ->addColumn('type', array('title' => pht('Type'))) 40 + ->addColumn('name', array('title' => pht('Name'))); 39 41 40 - $durations = $api->getPatchDurations(); 42 + $durations = $api->getPatchDurations(); 41 43 42 - foreach ($patches as $patch) { 43 - $duration = idx($durations, $patch->getFullKey()); 44 - if ($duration === null) { 45 - $duration = '-'; 46 - } else { 47 - $duration = pht('%s us', new PhutilNumber($duration)); 44 + foreach ($patches as $patch) { 45 + $duration = idx($durations, $patch->getFullKey()); 46 + if ($duration === null) { 47 + $duration = '-'; 48 + } else { 49 + $duration = pht('%s us', new PhutilNumber($duration)); 50 + } 51 + 52 + $table->addRow(array( 53 + 'id' => $patch->getFullKey(), 54 + 'host' => $ref->getRefKey(), 55 + 'status' => in_array($patch->getFullKey(), $applied) 56 + ? pht('Applied') 57 + : pht('Not Applied'), 58 + 'duration' => $duration, 59 + 'type' => $patch->getType(), 60 + 'name' => $patch->getName(), 61 + )); 48 62 } 49 63 50 - $table->addRow(array( 51 - 'id' => $patch->getFullKey(), 52 - 'status' => in_array($patch->getFullKey(), $applied) 53 - ? pht('Applied') 54 - : pht('Not Applied'), 55 - 'duration' => $duration, 56 - 'type' => $patch->getType(), 57 - 'name' => $patch->getName(), 58 - )); 64 + $table->draw(); 59 65 } 60 - 61 - $table->draw(); 62 66 return 0; 63 67 } 64 68
+16 -8
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php
··· 73 73 $init_only = $args->getArg('init-only'); 74 74 $no_adjust = $args->getArg('no-adjust'); 75 75 76 - $this->upgradeSchemata($apply_only, $no_quickstart, $init_only); 76 + $apis = $this->getMasterAPIs(); 77 77 78 - if ($no_adjust || $init_only || $apply_only) { 79 - $console->writeOut( 80 - "%s\n", 81 - pht('Declining to apply storage adjustments.')); 82 - return 0; 83 - } else { 84 - return $this->adjustSchemata(false); 78 + foreach ($apis as $api) { 79 + $this->upgradeSchemata($api, $apply_only, $no_quickstart, $init_only); 80 + 81 + if ($no_adjust || $init_only || $apply_only) { 82 + $console->writeOut( 83 + "%s\n", 84 + pht('Declining to apply storage adjustments.')); 85 + } else { 86 + $err = $this->adjustSchemata($api, false); 87 + if ($err) { 88 + return $err; 89 + } 90 + } 85 91 } 92 + 93 + return 0; 86 94 } 87 95 88 96 }
+86 -31
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php
··· 3 3 abstract class PhabricatorStorageManagementWorkflow 4 4 extends PhabricatorManagementWorkflow { 5 5 6 - private $api; 6 + private $apis = array(); 7 7 private $dryRun; 8 8 private $force; 9 9 private $patches; 10 10 11 11 private $didInitialize; 12 12 13 - final public function getAPI() { 14 - return $this->api; 13 + final public function setAPIs(array $apis) { 14 + $this->apis = $apis; 15 + return $this; 16 + } 17 + 18 + final public function getAnyAPI() { 19 + return head($this->getAPIs()); 20 + } 21 + 22 + final public function getMasterAPIs() { 23 + $apis = $this->getAPIs(); 24 + 25 + $results = array(); 26 + foreach ($apis as $api) { 27 + if ($api->getRef()->getIsMaster()) { 28 + $results[] = $api; 29 + } 30 + } 31 + 32 + if (!$results) { 33 + throw new PhutilArgumentUsageException( 34 + pht( 35 + 'This command only operates on database masters, but the selected '. 36 + 'database hosts do not include any masters.')); 37 + } 38 + 39 + return $results; 40 + } 41 + 42 + final public function getSingleAPI() { 43 + $apis = $this->getAPIs(); 44 + if (count($apis) == 1) { 45 + return head($apis); 46 + } 47 + 48 + throw new PhutilArgumentUsageException( 49 + pht( 50 + 'Phabricator is configured in cluster mode, with multiple database '. 51 + 'hosts. Use "--host" to specify which host you want to operate on.')); 15 52 } 16 53 17 - final public function setAPI(PhabricatorStorageManagementAPI $api) { 18 - $this->api = $api; 19 - return $this; 54 + final public function getAPIs() { 55 + return $this->apis; 20 56 } 21 57 22 58 final protected function isDryRun() { ··· 73 109 74 110 public function didExecute(PhutilArgumentParser $args) {} 75 111 76 - private function loadSchemata() { 77 - $query = id(new PhabricatorConfigSchemaQuery()) 78 - ->setAPI($this->getAPI()); 112 + private function loadSchemata(PhabricatorStorageManagementAPI $api) { 113 + $query = id(new PhabricatorConfigSchemaQuery()); 114 + 115 + $ref = $api->getRef(); 116 + $ref_key = $ref->getRefKey(); 117 + 118 + $query->setAPIs(array($api)); 119 + $query->setRefs(array($ref)); 79 120 80 - $actual = $query->loadActualSchema(); 81 - $expect = $query->loadExpectedSchema(); 82 - $comp = $query->buildComparisonSchema($expect, $actual); 121 + $actual = $query->loadActualSchemata(); 122 + $expect = $query->loadExpectedSchemata(); 123 + $comp = $query->buildComparisonSchemata($expect, $actual); 83 124 84 - return array($comp, $expect, $actual); 125 + return array( 126 + $comp[$ref_key], 127 + $expect[$ref_key], 128 + $actual[$ref_key], 129 + ); 85 130 } 86 131 87 - final protected function adjustSchemata($unsafe) { 88 - $lock = $this->lock(); 132 + final protected function adjustSchemata( 133 + PhabricatorStorageManagementAPI $api, 134 + $unsafe) { 135 + 136 + $lock = $this->lock($api); 89 137 90 138 try { 91 - $err = $this->doAdjustSchemata($unsafe); 139 + $err = $this->doAdjustSchemata($api, $unsafe); 92 140 } catch (Exception $ex) { 93 141 $lock->unlock(); 94 142 throw $ex; ··· 99 147 return $err; 100 148 } 101 149 102 - final private function doAdjustSchemata($unsafe) { 150 + final private function doAdjustSchemata( 151 + PhabricatorStorageManagementAPI $api, 152 + $unsafe) { 153 + 103 154 $console = PhutilConsole::getConsole(); 104 155 105 156 $console->writeOut( 106 157 "%s\n", 107 - pht('Verifying database schemata...')); 158 + pht( 159 + 'Verifying database schemata on "%s"...', 160 + $api->getRef()->getRefKey())); 108 161 109 - list($adjustments, $errors) = $this->findAdjustments(); 110 - $api = $this->getAPI(); 162 + list($adjustments, $errors) = $this->findAdjustments($api); 111 163 112 164 if (!$adjustments) { 113 165 $console->writeOut( ··· 415 467 return $this->printErrors($errors, $err); 416 468 } 417 469 418 - private function findAdjustments() { 419 - list($comp, $expect, $actual) = $this->loadSchemata(); 470 + private function findAdjustments( 471 + PhabricatorStorageManagementAPI $api) { 472 + list($comp, $expect, $actual) = $this->loadSchemata($api); 420 473 421 474 $issue_charset = PhabricatorConfigStorageSchema::ISSUE_CHARSET; 422 475 $issue_collation = PhabricatorConfigStorageSchema::ISSUE_COLLATION; ··· 766 819 } 767 820 768 821 final protected function upgradeSchemata( 822 + PhabricatorStorageManagementAPI $api, 769 823 $apply_only = null, 770 824 $no_quickstart = false, 771 825 $init_only = false) { 772 826 773 - $lock = $this->lock(); 827 + $lock = $this->lock($api); 774 828 775 829 try { 776 - $this->doUpgradeSchemata($apply_only, $no_quickstart, $init_only); 830 + $this->doUpgradeSchemata($api, $apply_only, $no_quickstart, $init_only); 777 831 } catch (Exception $ex) { 778 832 $lock->unlock(); 779 833 throw $ex; ··· 783 837 } 784 838 785 839 final private function doUpgradeSchemata( 840 + PhabricatorStorageManagementAPI $api, 786 841 $apply_only, 787 842 $no_quickstart, 788 843 $init_only) { 789 844 790 - $api = $this->getAPI(); 791 - 792 - $applied = $this->getApi()->getAppliedPatches(); 845 + $applied = $api->getAppliedPatches(); 793 846 if ($applied === null) { 794 847 if ($this->dryRun) { 795 848 echo pht( ··· 923 976 if (count($this->patches)) { 924 977 throw new Exception( 925 978 pht( 926 - 'Some patches could not be applied: %s', 979 + 'Some patches could not be applied to "%s": %s', 980 + $api->getRef()->getRefKey(), 927 981 implode(', ', array_keys($this->patches)))); 928 982 } else if (!$this->dryRun && !$apply_only) { 929 983 echo pht( 930 - "Storage is up to date. Use '%s' for details.", 984 + 'Storage is up to date on "%s". Use "%s" for details.', 985 + $api->getRef()->getRefKey(), 931 986 'storage status')."\n"; 932 987 } 933 988 break; ··· 955 1010 * 956 1011 * @return PhabricatorGlobalLock 957 1012 */ 958 - final protected function lock() { 1013 + final protected function lock(PhabricatorStorageManagementAPI $api) { 959 1014 return PhabricatorGlobalLock::newLock(__CLASS__) 960 - ->useSpecificConnection($this->getApi()->getConn(null)) 1015 + ->useSpecificConnection($api->getConn(null)) 961 1016 ->lock(); 962 1017 } 963 1018