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

Provide a simple read-only maintenance mode for repositories

Summary:
Ref T13393. While doing a shard migration in the Phacility cluster, we'd like to stop writes to the migrating repository. It's safe to continue serving reads.

Add a simple maintenance mode for making repositories completely read-only during maintenance.

Test Plan: Put a repository into read-only mode, tried to write via HTTP + SSH. Viewed web UI. Took it back out of maintenance mode.

Maniphest Tasks: T13393

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

+223 -5
+4
src/__phutil_library_map__.php
··· 4470 4470 'PhabricatorRepositoryIdentityTransaction' => 'applications/repository/storage/PhabricatorRepositoryIdentityTransaction.php', 4471 4471 'PhabricatorRepositoryIdentityTransactionQuery' => 'applications/repository/query/PhabricatorRepositoryIdentityTransactionQuery.php', 4472 4472 'PhabricatorRepositoryIdentityTransactionType' => 'applications/repository/xaction/PhabricatorRepositoryIdentityTransactionType.php', 4473 + 'PhabricatorRepositoryMaintenanceTransaction' => 'applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php', 4473 4474 'PhabricatorRepositoryManagementCacheWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementCacheWorkflow.php', 4474 4475 'PhabricatorRepositoryManagementClusterizeWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementClusterizeWorkflow.php', 4475 4476 'PhabricatorRepositoryManagementDiscoverWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementDiscoverWorkflow.php', ··· 4478 4479 'PhabricatorRepositoryManagementListPathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListPathsWorkflow.php', 4479 4480 'PhabricatorRepositoryManagementListWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php', 4480 4481 'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php', 4482 + 'PhabricatorRepositoryManagementMaintenanceWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php', 4481 4483 'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php', 4482 4484 'PhabricatorRepositoryManagementMarkReachableWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php', 4483 4485 'PhabricatorRepositoryManagementMirrorWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php', ··· 10911 10913 'PhabricatorRepositoryIdentityTransaction' => 'PhabricatorModularTransaction', 10912 10914 'PhabricatorRepositoryIdentityTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 10913 10915 'PhabricatorRepositoryIdentityTransactionType' => 'PhabricatorModularTransactionType', 10916 + 'PhabricatorRepositoryMaintenanceTransaction' => 'PhabricatorRepositoryTransactionType', 10914 10917 'PhabricatorRepositoryManagementCacheWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10915 10918 'PhabricatorRepositoryManagementClusterizeWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10916 10919 'PhabricatorRepositoryManagementDiscoverWorkflow' => 'PhabricatorRepositoryManagementWorkflow', ··· 10919 10922 'PhabricatorRepositoryManagementListPathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10920 10923 'PhabricatorRepositoryManagementListWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10921 10924 'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10925 + 'PhabricatorRepositoryManagementMaintenanceWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10922 10926 'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10923 10927 'PhabricatorRepositoryManagementMarkReachableWorkflow' => 'PhabricatorRepositoryManagementWorkflow', 10924 10928 'PhabricatorRepositoryManagementMirrorWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
+20 -5
src/applications/diffusion/controller/DiffusionRepositoryController.php
··· 145 145 ->setRight(array($this->branchButton, $actions_button, $clone_button)) 146 146 ->addClass('diffusion-action-bar'); 147 147 148 + $status_view = null; 149 + if ($repository->isReadOnly()) { 150 + $status_view = id(new PHUIInfoView()) 151 + ->setSeverity(PHUIInfoView::SEVERITY_WARNING) 152 + ->setErrors( 153 + array( 154 + phutil_escape_html_newlines( 155 + $repository->getReadOnlyMessageForDisplay()), 156 + )); 157 + } 158 + 148 159 $view = id(new PHUITwoColumnView()) 149 160 ->setHeader($header) 150 - ->setFooter(array( 151 - $bar, 152 - $description, 153 - $content, 154 - )); 161 + ->setFooter( 162 + array( 163 + $status_view, 164 + $bar, 165 + $description, 166 + $content, 167 + )); 155 168 156 169 if ($page_has_content) { 157 170 $view->setTabs($tabs); ··· 327 340 328 341 if (!$repository->isTracked()) { 329 342 $header->setStatus('fa-ban', 'dark', pht('Inactive')); 343 + } else if ($repository->isReadOnly()) { 344 + $header->setStatus('fa-wrench', 'indigo', pht('Under Maintenance')); 330 345 } else if ($repository->isImporting()) { 331 346 $ratio = $repository->loadImportProgress(); 332 347 $percentage = sprintf('%.2f%%', 100 * $ratio);
+6
src/applications/diffusion/controller/DiffusionServeController.php
··· 302 302 } 303 303 304 304 if ($is_push) { 305 + if ($repository->isReadOnly()) { 306 + return new PhabricatorVCSResponse( 307 + 503, 308 + $repository->getReadOnlyMessageForDisplay()); 309 + } 310 + 305 311 $can_write = 306 312 $repository->canServeProtocol($proto_https, true) || 307 313 $repository->canServeProtocol($proto_http, true);
+4
src/applications/diffusion/ssh/DiffusionSSHWorkflow.php
··· 255 255 'user account.')); 256 256 } 257 257 258 + if ($repository->isReadOnly()) { 259 + throw new Exception($repository->getReadOnlyMessageForDisplay()); 260 + } 261 + 258 262 $protocol = PhabricatorRepositoryURI::BUILTIN_PROTOCOL_SSH; 259 263 if ($repository->canServeProtocol($protocol, true)) { 260 264 $can_push = PhabricatorPolicyFilter::hasCapability(
+7
src/applications/repository/engine/PhabricatorRepositoryPullEngine.php
··· 52 52 $repository = $this->getRepository(); 53 53 $viewer = PhabricatorUser::getOmnipotentUser(); 54 54 55 + if ($repository->isReadOnly()) { 56 + $this->skipPull( 57 + pht( 58 + "Skipping pull on read-only repository.\n\n%s", 59 + $repository->getReadOnlyMessageForDisplay())); 60 + } 61 + 55 62 $is_hg = false; 56 63 $is_git = false; 57 64 $is_svn = false;
+104
src/applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryManagementMaintenanceWorkflow 4 + extends PhabricatorRepositoryManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('maintenance') 9 + ->setExamples( 10 + "**maintenance** --start __message__ __repository__ ...\n". 11 + "**maintenance** --stop __repository__") 12 + ->setSynopsis( 13 + pht('Set or clear read-only mode for repository maintenance.')) 14 + ->setArguments( 15 + array( 16 + array( 17 + 'name' => 'start', 18 + 'param' => 'message', 19 + 'help' => pht( 20 + 'Put repositories into maintenance mode.'), 21 + ), 22 + array( 23 + 'name' => 'stop', 24 + 'help' => pht( 25 + 'Take repositories out of maintenance mode, returning them '. 26 + 'to normal serice.'), 27 + ), 28 + array( 29 + 'name' => 'repositories', 30 + 'wildcard' => true, 31 + ), 32 + )); 33 + } 34 + 35 + public function execute(PhutilArgumentParser $args) { 36 + $viewer = $this->getViewer(); 37 + 38 + $repositories = $this->loadRepositories($args, 'repositories'); 39 + if (!$repositories) { 40 + throw new PhutilArgumentUsageException( 41 + pht('Specify one or more repositories to act on.')); 42 + } 43 + 44 + $message = $args->getArg('start'); 45 + $is_start = (bool)strlen($message); 46 + $is_stop = $args->getArg('stop'); 47 + 48 + if (!$is_start && !$is_stop) { 49 + throw new PhutilArgumentUsageException( 50 + pht( 51 + 'Use "--start <message>" to put repositories into maintenance '. 52 + 'mode, or "--stop" to take them out of maintenance mode.')); 53 + } 54 + 55 + if ($is_start && $is_stop) { 56 + throw new PhutilArgumentUsageException( 57 + pht( 58 + 'Specify either "--start" or "--stop", but not both.')); 59 + } 60 + 61 + $content_source = $this->newContentSource(); 62 + $diffusion_phid = id(new PhabricatorDiffusionApplication())->getPHID(); 63 + 64 + if ($is_start) { 65 + $new_value = $message; 66 + } else { 67 + $new_value = null; 68 + } 69 + 70 + foreach ($repositories as $repository) { 71 + $xactions = array(); 72 + 73 + $xactions[] = $repository->getApplicationTransactionTemplate() 74 + ->setTransactionType( 75 + PhabricatorRepositoryMaintenanceTransaction::TRANSACTIONTYPE) 76 + ->setNewValue($new_value); 77 + 78 + $repository->getApplicationTransactionEditor() 79 + ->setActor($viewer) 80 + ->setActingAsPHID($diffusion_phid) 81 + ->setContentSource($content_source) 82 + ->setContinueOnNoEffect(true) 83 + ->setContinueOnMissingFields(true) 84 + ->applyTransactions($repository, $xactions); 85 + 86 + if ($is_start) { 87 + echo tsprintf( 88 + "%s\n", 89 + pht( 90 + 'Put repository "%s" into maintenance mode.', 91 + $repository->getDisplayName())); 92 + } else { 93 + echo tsprintf( 94 + "%s\n", 95 + pht( 96 + 'Took repository "%s" out of maintenance mode.', 97 + $repository->getDisplayName())); 98 + } 99 + } 100 + 101 + return 0; 102 + } 103 + 104 + }
+35
src/applications/repository/storage/PhabricatorRepository.php
··· 1410 1410 } 1411 1411 } 1412 1412 1413 + if ($write) { 1414 + if ($this->isReadOnly()) { 1415 + return false; 1416 + } 1417 + } 1418 + 1413 1419 return false; 1414 1420 } 1415 1421 ··· 2264 2270 2265 2271 public function supportsBranchComparison() { 2266 2272 return $this->isGit(); 2273 + } 2274 + 2275 + public function isReadOnly() { 2276 + return (bool)$this->getDetail('read-only'); 2277 + } 2278 + 2279 + public function setReadOnly($read_only) { 2280 + return $this->setDetail('read-only', $read_only); 2281 + } 2282 + 2283 + public function getReadOnlyMessage() { 2284 + return $this->getDetail('read-only-message'); 2285 + } 2286 + 2287 + public function setReadOnlyMessage($message) { 2288 + return $this->setDetail('read-only-message', $message); 2289 + } 2290 + 2291 + public function getReadOnlyMessageForDisplay() { 2292 + $parts = array(); 2293 + $parts[] = pht( 2294 + 'This repository is currently in read-only maintenance mode.'); 2295 + 2296 + $message = $this->getReadOnlyMessage(); 2297 + if ($message !== null) { 2298 + $parts[] = $message; 2299 + } 2300 + 2301 + return implode("\n\n", $parts); 2267 2302 } 2268 2303 2269 2304 /* -( Repository URIs )---------------------------------------------------- */
+43
src/applications/repository/xaction/PhabricatorRepositoryMaintenanceTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorRepositoryMaintenanceTransaction 4 + extends PhabricatorRepositoryTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'maintenance'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getReadOnlyMessage(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + if ($value === null) { 14 + $object 15 + ->setReadOnly(false) 16 + ->setReadOnlyMessage(null); 17 + } else { 18 + $object 19 + ->setReadOnly(true) 20 + ->setReadOnlyMessage($value); 21 + } 22 + } 23 + 24 + public function getTitle() { 25 + $old = $this->getOldValue(); 26 + $new = $this->getNewValue(); 27 + 28 + if (strlen($old) && !strlen($new)) { 29 + return pht( 30 + '%s took this repository out of maintenance mode.', 31 + $this->renderAuthor()); 32 + } else if (!strlen($old) && strlen($new)) { 33 + return pht( 34 + '%s put this repository into maintenance mode.', 35 + $this->renderAuthor()); 36 + } else { 37 + return pht( 38 + '%s updated the maintenance message for this repository.', 39 + $this->renderAuthor()); 40 + } 41 + } 42 + 43 + }