diff --git a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php --- a/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryCreateController.php @@ -20,6 +20,7 @@ // the latter two cases, we show only a few of the pages. $repository = null; + $service = null; switch ($this->edit) { case 'remote': case 'policy': @@ -40,6 +41,38 @@ $this->requireApplicationCapability( DiffusionCreateRepositoriesCapability::CAPABILITY); + // Pick a random open service to allocate this repository on, if any + // exist. If there are no services, we aren't in cluster mode and + // will allocate locally. If there are services but none permit + // allocations, we fail. + $services = id(new AlmanacServiceQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withServiceClasses( + array( + 'AlmanacClusterRepositoryServiceType', + )) + ->execute(); + if ($services) { + // Filter out services which do not permit new allocations. + foreach ($services as $key => $possible_service) { + if ($possible_service->getAlmanacPropertyValue('closed')) { + unset($services[$key]); + } + } + + if (!$services) { + throw new Exception( + pht( + 'This install is configured in cluster mode, but all '. + 'available repository cluster services are closed to new '. + 'allocations. At least one service must be open to allow '. + 'new allocations to take place.')); + } + + shuffle($services); + $service = head($services); + } + $cancel_uri = $this->getApplicationURI('new/'); break; default: @@ -110,6 +143,7 @@ $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; $type_push = PhabricatorRepositoryTransaction::TYPE_PUSH_POLICY; + $type_service = PhabricatorRepositoryTransaction::TYPE_SERVICE; $xactions = array(); @@ -141,8 +175,13 @@ ->getControl('activate')->getValue(); $xactions[] = id(clone $template) ->setTransactionType($type_activate) - ->setNewValue( - ($activate == 'start')); + ->setNewValue(($activate == 'start')); + + if ($service) { + $xactions[] = id(clone $template) + ->setTransactionType($type_service) + ->setNewValue($service->getPHID()); + } $default_local_path = PhabricatorEnv::getEnvConfig( 'repository.default-local-path'); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php --- a/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditMainController.php @@ -575,6 +575,21 @@ ->setUser($viewer) ->setActionList($actions); + $service_phid = $repository->getAlmanacServicePHID(); + if ($service_phid) { + $handles = $this->loadViewerHandles(array($service_phid)); + $v_service = $handles[$service_phid]->renderLink(); + } else { + $v_service = phutil_tag( + 'em', + array(), + pht('Local')); + } + + $view->addProperty( + pht('Storage Service'), + $v_service); + $view->addProperty( pht('Storage Path'), $repository->getHumanReadableDetail('local-path')); diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php --- a/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditStorageController.php @@ -33,8 +33,28 @@ $title = pht('Edit %s', $repository->getName()); + $service_phid = $repository->getAlmanacServicePHID(); + if ($service_phid) { + $handles = $this->loadViewerHandles(array($service_phid)); + $v_service = $handles[$service_phid]->renderLink(); + } else { + $v_service = phutil_tag( + 'em', + array(), + pht('Local')); + } + $form = id(new AphrontFormView()) ->setUser($user) + ->appendChild( + id(new AphrontFormMarkupControl()) + ->setLabel(pht('Storage Service')) + ->setValue($v_service)) + ->appendChild( + id(new AphrontFormMarkupControl()) + ->setName('local') + ->setLabel(pht('Storage Path')) + ->setValue($v_local)) ->appendRemarkupInstructions( pht( "You can not adjust the local path for this repository from the ". @@ -43,11 +63,6 @@ $repository->getCallsign(), $user->getUsername())) ->appendChild( - id(new AphrontFormMarkupControl()) - ->setName('local') - ->setLabel(pht('Local Path')) - ->setValue($v_local)) - ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($edit_uri, pht('Done'))); diff --git a/src/applications/repository/editor/PhabricatorRepositoryEditor.php b/src/applications/repository/editor/PhabricatorRepositoryEditor.php --- a/src/applications/repository/editor/PhabricatorRepositoryEditor.php +++ b/src/applications/repository/editor/PhabricatorRepositoryEditor.php @@ -40,6 +40,7 @@ $types[] = PhabricatorRepositoryTransaction::TYPE_CREDENTIAL; $types[] = PhabricatorRepositoryTransaction::TYPE_DANGEROUS; $types[] = PhabricatorRepositoryTransaction::TYPE_CLONE_NAME; + $types[] = PhabricatorRepositoryTransaction::TYPE_SERVICE; $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -95,6 +96,8 @@ return $object->shouldAllowDangerousChanges(); case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: return $object->getDetail('clone-name'); + case PhabricatorRepositoryTransaction::TYPE_SERVICE: + return $object->getAlmanacServicePHID(); } } @@ -127,6 +130,7 @@ case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: + case PhabricatorRepositoryTransaction::TYPE_SERVICE: return $xaction->getNewValue(); case PhabricatorRepositoryTransaction::TYPE_NOTIFY: case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: @@ -198,6 +202,9 @@ case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: $object->setDetail('clone-name', $xaction->getNewValue()); return; + case PhabricatorRepositoryTransaction::TYPE_SERVICE: + $object->setAlmanacServicePHID($xaction->getNewValue()); + return; case PhabricatorRepositoryTransaction::TYPE_ENCODING: // Make sure the encoding is valid by converting to UTF-8. This tests // that the user has mbstring installed, and also that they didn't type @@ -306,6 +313,7 @@ case PhabricatorRepositoryTransaction::TYPE_CREDENTIAL: case PhabricatorRepositoryTransaction::TYPE_DANGEROUS: case PhabricatorRepositoryTransaction::TYPE_CLONE_NAME: + case PhabricatorRepositoryTransaction::TYPE_SERVICE: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, diff --git a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php --- a/src/applications/repository/storage/PhabricatorRepositoryTransaction.php +++ b/src/applications/repository/storage/PhabricatorRepositoryTransaction.php @@ -24,6 +24,7 @@ const TYPE_CREDENTIAL = 'repo:credential'; const TYPE_DANGEROUS = 'repo:dangerous'; const TYPE_CLONE_NAME = 'repo:clone-name'; + const TYPE_SERVICE = 'repo:service'; // TODO: Clean up these legacy transaction types. const TYPE_SSH_LOGIN = 'repo:ssh-login'; @@ -52,8 +53,13 @@ switch ($this->getTransactionType()) { case self::TYPE_PUSH_POLICY: - $phids[] = $old; - $phids[] = $new; + case self::TYPE_SERVICE: + if ($old) { + $phids[] = $old; + } + if ($new) { + $phids[] = $new; + } break; } @@ -367,6 +373,26 @@ $old, $new); } + case self::TYPE_SERVICE: + if (strlen($old) && !strlen($new)) { + return pht( + '%s moved storage for this repository from %s to local.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($old)); + } else if (!strlen($old) && strlen($new)) { + // TODO: Possibly, we should distinguish between automatic assignment + // on creation vs explicit adjustment. + return pht( + '%s set storage for this repository to %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($new)); + } else { + return pht( + '%s moved storage for this repository from %s to %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($old), + $this->renderHandleLink($new)); + } } return parent::getTitle();