mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Allow Almanac bindings to be disabled and unused interfaces to be removed
Summary: Fixes T9762. Ref T10246. **Disabling Bindings**: Previously, there was no formal way to disable bindings. The internal callers sometimes check some informal property on the binding, but this is a common need and deserves first-class support in the UI. Allow bindings to be disabled. **Deleting Interfaces**: Previously, you could not delete interfaces. Now, you can delete unused interfaces. Also some minor cleanup and slightly less mysterious documentation. Test Plan: Disabled bindings and deleted interfaces. Reviewers: chad Reviewed By: chad Subscribers: yelirekim Maniphest Tasks: T9762, T10246 Differential Revision: https://secure.phabricator.com/D15345
This commit is contained in:
parent
5b9d8aeae7
commit
4c97d88aa4
19 changed files with 357 additions and 14 deletions
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_binding
|
||||
ADD isDisabled BOOL NOT NULL;
|
|
@ -11,6 +11,7 @@ phutil_register_library_map(array(
|
|||
'class' => array(
|
||||
'AlmanacAddress' => 'applications/almanac/util/AlmanacAddress.php',
|
||||
'AlmanacBinding' => 'applications/almanac/storage/AlmanacBinding.php',
|
||||
'AlmanacBindingDisableController' => 'applications/almanac/controller/AlmanacBindingDisableController.php',
|
||||
'AlmanacBindingEditController' => 'applications/almanac/controller/AlmanacBindingEditController.php',
|
||||
'AlmanacBindingEditor' => 'applications/almanac/editor/AlmanacBindingEditor.php',
|
||||
'AlmanacBindingPHIDType' => 'applications/almanac/phid/AlmanacBindingPHIDType.php',
|
||||
|
@ -51,6 +52,7 @@ phutil_register_library_map(array(
|
|||
'AlmanacEditor' => 'applications/almanac/editor/AlmanacEditor.php',
|
||||
'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php',
|
||||
'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php',
|
||||
'AlmanacInterfaceDeleteController' => 'applications/almanac/controller/AlmanacInterfaceDeleteController.php',
|
||||
'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php',
|
||||
'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
|
||||
'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
|
||||
|
@ -3996,6 +3998,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
),
|
||||
'AlmanacBindingDisableController' => 'AlmanacServiceController',
|
||||
'AlmanacBindingEditController' => 'AlmanacServiceController',
|
||||
'AlmanacBindingEditor' => 'AlmanacEditor',
|
||||
'AlmanacBindingPHIDType' => 'PhabricatorPHIDType',
|
||||
|
@ -4049,8 +4052,10 @@ phutil_register_library_map(array(
|
|||
'AlmanacDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
),
|
||||
'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController',
|
||||
'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
|
||||
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
|
||||
'AlmanacInterfaceQuery' => 'AlmanacQuery',
|
||||
|
|
|
@ -55,9 +55,11 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
|||
),
|
||||
'interface/' => array(
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceEditController',
|
||||
'delete/(?:(?P<id>\d+)/)?' => 'AlmanacInterfaceDeleteController',
|
||||
),
|
||||
'binding/' => array(
|
||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacBindingEditController',
|
||||
'disable/(?:(?P<id>\d+)/)?' => 'AlmanacBindingDisableController',
|
||||
'(?P<id>\d+)/' => 'AlmanacBindingViewController',
|
||||
),
|
||||
'network/' => array(
|
||||
|
@ -80,6 +82,17 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
|||
}
|
||||
|
||||
protected function getCustomCapabilities() {
|
||||
$cluster_caption = pht(
|
||||
'This permission is very dangerous. %s',
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => PhabricatorEnv::getDoclink(
|
||||
'User Guide: Phabricator Clusters'),
|
||||
'target' => '_blank',
|
||||
),
|
||||
pht('Learn More')));
|
||||
|
||||
return array(
|
||||
AlmanacCreateServicesCapability::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
|
@ -94,7 +107,8 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
|||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
),
|
||||
AlmanacManageClusterServicesCapability::CAPABILITY => array(
|
||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||
'default' => PhabricatorPolicies::POLICY_NOONE,
|
||||
'caption' => $cluster_caption,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacBindingDisableController
|
||||
extends AlmanacServiceController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
$binding = id(new AlmanacBindingQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$binding) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$id = $binding->getID();
|
||||
$is_disable = !$binding->getIsDisabled();
|
||||
$done_uri = $binding->getURI();
|
||||
|
||||
if ($is_disable) {
|
||||
$disable_title = pht('Disable Binding');
|
||||
$disable_body = pht('Disable this binding?');
|
||||
$disable_button = pht('Disable Binding');
|
||||
|
||||
$v_disable = 1;
|
||||
} else {
|
||||
$disable_title = pht('Enable Binding');
|
||||
$disable_body = pht('Enable this binding?');
|
||||
$disable_button = pht('Enable Binding');
|
||||
|
||||
$v_disable = 0;
|
||||
}
|
||||
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$type_disable = AlmanacBindingTransaction::TYPE_DISABLE;
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new AlmanacBindingTransaction())
|
||||
->setTransactionType($type_disable)
|
||||
->setNewValue($v_disable);
|
||||
|
||||
$editor = id(new AlmanacBindingEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$editor->applyTransactions($binding, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($done_uri);
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle($disable_title)
|
||||
->appendParagraph($disable_body)
|
||||
->addSubmitButton($disable_button)
|
||||
->addCancelButton($done_uri);
|
||||
}
|
||||
|
||||
}
|
|
@ -35,6 +35,10 @@ final class AlmanacBindingViewController
|
|||
->setHeader($title)
|
||||
->setPolicyObject($binding);
|
||||
|
||||
if ($binding->getIsDisabled()) {
|
||||
$header->setStatus('fa-ban', 'red', pht('Disabled'));
|
||||
}
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->addPropertyList($property_list);
|
||||
|
@ -114,6 +118,24 @@ final class AlmanacBindingViewController
|
|||
->setWorkflow(!$can_edit)
|
||||
->setDisabled(!$can_edit));
|
||||
|
||||
if ($binding->getIsDisabled()) {
|
||||
$disable_icon = 'fa-check';
|
||||
$disable_text = pht('Enable Binding');
|
||||
} else {
|
||||
$disable_icon = 'fa-ban';
|
||||
$disable_text = pht('Disable Binding');
|
||||
}
|
||||
|
||||
$disable_href = $this->getApplicationURI("binding/disable/{$id}/");
|
||||
|
||||
$actions->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setIcon($disable_icon)
|
||||
->setName($disable_text)
|
||||
->setHref($disable_href)
|
||||
->setWorkflow(true)
|
||||
->setDisabled(!$can_edit));
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,7 +177,8 @@ abstract class AlmanacController
|
|||
$doc_link = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
|
||||
'href' => PhabricatorEnv::getDoclink(
|
||||
'User Guide: Phabricator Clusters'),
|
||||
'target' => '_blank',
|
||||
),
|
||||
pht('Learn More'));
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacInterfaceDeleteController
|
||||
extends AlmanacDeviceController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
$interface = id(new AlmanacInterfaceQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$interface) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$device = $interface->getDevice();
|
||||
$device_uri = $device->getURI();
|
||||
|
||||
if ($interface->loadIsInUse()) {
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Interface In Use'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'You can not delete this interface because it is currently in '.
|
||||
'use. One or more services are bound to it.'))
|
||||
->addCancelButton($device_uri);
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$type_interface = AlmanacDeviceTransaction::TYPE_INTERFACE;
|
||||
|
||||
$xactions = array();
|
||||
|
||||
$v_old = array(
|
||||
'id' => $interface->getID(),
|
||||
) + $interface->toAddress()->toDictionary();
|
||||
|
||||
$xactions[] = id(new AlmanacDeviceTransaction())
|
||||
->setTransactionType($type_interface)
|
||||
->setOldValue($v_old)
|
||||
->setNewValue(null);
|
||||
|
||||
$editor = id(new AlmanacDeviceEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true);
|
||||
|
||||
$editor->applyTransactions($device, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI($device_uri);
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Delete Interface'))
|
||||
->appendParagraph(
|
||||
pht(
|
||||
'Remove interface %s on device %s?',
|
||||
phutil_tag('strong', array(), $interface->renderDisplayAddress()),
|
||||
phutil_tag('strong', array(), $device->getName())))
|
||||
->addCancelButton($device_uri)
|
||||
->addSubmitButton(pht('Delete Interface'));
|
||||
}
|
||||
|
||||
}
|
|
@ -122,7 +122,8 @@ final class AlmanacServiceViewController
|
|||
->setNoDataString(
|
||||
pht('This service has not been bound to any device interfaces yet.'))
|
||||
->setUser($viewer)
|
||||
->setBindings($bindings);
|
||||
->setBindings($bindings)
|
||||
->setHideServiceColumn(true);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Service Bindings'))
|
||||
|
|
|
@ -13,6 +13,7 @@ final class AlmanacBindingEditor
|
|||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = AlmanacBindingTransaction::TYPE_INTERFACE;
|
||||
$types[] = AlmanacBindingTransaction::TYPE_DISABLE;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
@ -23,6 +24,8 @@ final class AlmanacBindingEditor
|
|||
switch ($xaction->getTransactionType()) {
|
||||
case AlmanacBindingTransaction::TYPE_INTERFACE:
|
||||
return $object->getInterfacePHID();
|
||||
case AlmanacBindingTransaction::TYPE_DISABLE:
|
||||
return $object->getIsDisabled();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||
|
@ -35,6 +38,8 @@ final class AlmanacBindingEditor
|
|||
switch ($xaction->getTransactionType()) {
|
||||
case AlmanacBindingTransaction::TYPE_INTERFACE:
|
||||
return $xaction->getNewValue();
|
||||
case AlmanacBindingTransaction::TYPE_DISABLE:
|
||||
return (int)$xaction->getNewValue();
|
||||
}
|
||||
|
||||
return parent::getCustomTransactionNewValue($object, $xaction);
|
||||
|
@ -53,6 +58,9 @@ final class AlmanacBindingEditor
|
|||
$object->setDevicePHID($interface->getDevicePHID());
|
||||
$object->setInterfacePHID($interface->getPHID());
|
||||
return;
|
||||
case AlmanacBindingTransaction::TYPE_DISABLE:
|
||||
$object->setIsDisabled($xaction->getNewValue());
|
||||
return;
|
||||
}
|
||||
|
||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||
|
@ -63,6 +71,8 @@ final class AlmanacBindingEditor
|
|||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case AlmanacBindingTransaction::TYPE_DISABLE:
|
||||
return;
|
||||
case AlmanacBindingTransaction::TYPE_INTERFACE:
|
||||
$interface_phids = array();
|
||||
|
||||
|
|
|
@ -310,6 +310,19 @@ final class AlmanacDeviceEditor
|
|||
pht('You can not edit an invalid or restricted interface.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
continue;
|
||||
}
|
||||
|
||||
$new = $xaction->getNewValue();
|
||||
if (!$new) {
|
||||
if ($interface->loadIsInUse()) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('In Use'),
|
||||
pht('You can not delete an interface which is still in use.'),
|
||||
$xaction);
|
||||
$errors[] = $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ abstract class AlmanacSearchEngineAttachment
|
|||
'phid' => $binding->getPHID(),
|
||||
'properties' => $this->getAlmanacPropertyList($binding),
|
||||
'interface' => $this->getAlmanacInterfaceDictionary($interface),
|
||||
'disabled' => (bool)$binding->getIsDisabled(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ final class AlmanacBinding
|
|||
protected $devicePHID;
|
||||
protected $interfacePHID;
|
||||
protected $mailKey;
|
||||
protected $isDisabled;
|
||||
|
||||
private $service = self::ATTACHABLE;
|
||||
private $device = self::ATTACHABLE;
|
||||
|
@ -22,7 +23,8 @@ final class AlmanacBinding
|
|||
public static function initializeNewBinding(AlmanacService $service) {
|
||||
return id(new AlmanacBinding())
|
||||
->setServicePHID($service->getPHID())
|
||||
->attachAlmanacProperties(array());
|
||||
->attachAlmanacProperties(array())
|
||||
->setIsDisabled(0);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
|
@ -30,6 +32,7 @@ final class AlmanacBinding
|
|||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'mailKey' => 'bytes20',
|
||||
'isDisabled' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_service' => array(
|
||||
|
|
|
@ -4,6 +4,7 @@ final class AlmanacBindingTransaction
|
|||
extends AlmanacTransaction {
|
||||
|
||||
const TYPE_INTERFACE = 'almanac:binding:interface';
|
||||
const TYPE_DISABLE = 'almanac:binding:disable';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'almanac';
|
||||
|
@ -57,6 +58,17 @@ final class AlmanacBindingTransaction
|
|||
$this->renderHandleLink($new));
|
||||
}
|
||||
break;
|
||||
case self::TYPE_DISABLE:
|
||||
if ($new) {
|
||||
return pht(
|
||||
'%s disabled this binding.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
} else {
|
||||
return pht(
|
||||
'%s enabled this binding.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
|
|
|
@ -69,7 +69,7 @@ final class AlmanacDeviceTransaction
|
|||
return pht(
|
||||
'%s removed the interface %s from this device.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->describeInterface($new));
|
||||
$this->describeInterface($old));
|
||||
} else if ($new) {
|
||||
return pht(
|
||||
'%s added the interface %s to this device.',
|
||||
|
|
|
@ -4,7 +4,8 @@ final class AlmanacInterface
|
|||
extends AlmanacDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorDestructibleInterface {
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorExtendedPolicyInterface {
|
||||
|
||||
protected $devicePHID;
|
||||
protected $networkPHID;
|
||||
|
@ -74,6 +75,16 @@ final class AlmanacInterface
|
|||
return $this->getAddress().':'.$this->getPort();
|
||||
}
|
||||
|
||||
public function loadIsInUse() {
|
||||
$binding = id(new AlmanacBindingQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withInterfacePHIDs(array($this->getPHID()))
|
||||
->setLimit(1)
|
||||
->executeOne();
|
||||
|
||||
return (bool)$binding;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
@ -105,6 +116,27 @@ final class AlmanacInterface
|
|||
}
|
||||
|
||||
|
||||
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
|
||||
|
||||
|
||||
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
if ($this->getDevice()->isClusterDevice()) {
|
||||
return array(
|
||||
array(
|
||||
new PhabricatorAlmanacApplication(),
|
||||
AlmanacManageClusterServicesCapability::CAPABILITY,
|
||||
),
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ final class AlmanacBindingTableView extends AphrontView {
|
|||
private $bindings;
|
||||
private $noDataString;
|
||||
|
||||
private $hideServiceColumn;
|
||||
|
||||
public function setNoDataString($no_data_string) {
|
||||
$this->noDataString = $no_data_string;
|
||||
return $this;
|
||||
|
@ -23,6 +25,15 @@ final class AlmanacBindingTableView extends AphrontView {
|
|||
return $this->bindings;
|
||||
}
|
||||
|
||||
public function setHideServiceColumn($hide_service_column) {
|
||||
$this->hideServiceColumn = $hide_service_column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHideServiceColumn() {
|
||||
return $this->hideServiceColumn;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$bindings = $this->getBindings();
|
||||
$viewer = $this->getUser();
|
||||
|
@ -35,6 +46,22 @@ final class AlmanacBindingTableView extends AphrontView {
|
|||
}
|
||||
$handles = $viewer->loadHandles($phids);
|
||||
|
||||
$icon_disabled = id(new PHUIIconView())
|
||||
->setIcon('fa-ban')
|
||||
->addSigil('has-tooltip')
|
||||
->setMetadata(
|
||||
array(
|
||||
'tip' => pht('Disabled'),
|
||||
));
|
||||
|
||||
$icon_active = id(new PHUIIconView())
|
||||
->setIcon('fa-check')
|
||||
->addSigil('has-tooltip')
|
||||
->setMetadata(
|
||||
array(
|
||||
'tip' => pht('Active'),
|
||||
));
|
||||
|
||||
$rows = array();
|
||||
foreach ($bindings as $binding) {
|
||||
$addr = $binding->getInterface()->getAddress();
|
||||
|
@ -42,6 +69,7 @@ final class AlmanacBindingTableView extends AphrontView {
|
|||
|
||||
$rows[] = array(
|
||||
$binding->getID(),
|
||||
($binding->getIsDisabled() ? $icon_disabled : $icon_active),
|
||||
$handles->renderHandle($binding->getServicePHID()),
|
||||
$handles->renderHandle($binding->getDevicePHID()),
|
||||
$handles->renderHandle($binding->getInterface()->getNetworkPHID()),
|
||||
|
@ -61,6 +89,7 @@ final class AlmanacBindingTableView extends AphrontView {
|
|||
->setHeaders(
|
||||
array(
|
||||
pht('ID'),
|
||||
null,
|
||||
pht('Service'),
|
||||
pht('Device'),
|
||||
pht('Network'),
|
||||
|
@ -70,11 +99,18 @@ final class AlmanacBindingTableView extends AphrontView {
|
|||
->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'icon',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'wide',
|
||||
'action',
|
||||
))
|
||||
->setColumnVisibility(
|
||||
array(
|
||||
true,
|
||||
true,
|
||||
!$this->getHideServiceColumn(),
|
||||
));
|
||||
|
||||
return $table;
|
||||
|
|
|
@ -27,7 +27,9 @@ final class AlmanacInterfaceTableView extends AphrontView {
|
|||
$interfaces = $this->getInterfaces();
|
||||
$viewer = $this->getUser();
|
||||
|
||||
if ($this->getCanEdit()) {
|
||||
$can_edit = $this->getCanEdit();
|
||||
|
||||
if ($can_edit) {
|
||||
$button_class = 'small grey button';
|
||||
} else {
|
||||
$button_class = 'small grey button disabled';
|
||||
|
@ -42,13 +44,22 @@ final class AlmanacInterfaceTableView extends AphrontView {
|
|||
$handles->renderHandle($interface->getNetworkPHID()),
|
||||
$interface->getAddress(),
|
||||
$interface->getPort(),
|
||||
phutil_tag(
|
||||
javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => $button_class,
|
||||
'href' => '/almanac/interface/edit/'.$interface->getID().'/',
|
||||
'sigil' => ($can_edit ? null : 'workflow'),
|
||||
),
|
||||
pht('Edit')),
|
||||
javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => $button_class,
|
||||
'href' => '/almanac/interface/delete/'.$interface->getID().'/',
|
||||
'sigil' => 'workflow',
|
||||
),
|
||||
pht('Delete')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -60,6 +71,7 @@ final class AlmanacInterfaceTableView extends AphrontView {
|
|||
pht('Address'),
|
||||
pht('Port'),
|
||||
null,
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
|
@ -68,6 +80,7 @@ final class AlmanacInterfaceTableView extends AphrontView {
|
|||
'',
|
||||
'',
|
||||
'action',
|
||||
'action',
|
||||
));
|
||||
|
||||
return $table;
|
||||
|
|
|
@ -267,6 +267,11 @@ final class DrydockAlmanacServiceHostBlueprintImplementation
|
|||
|
||||
$free = array();
|
||||
foreach ($bindings as $binding) {
|
||||
// Don't consider disabled bindings to be available.
|
||||
if ($binding->getIsDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($allocated_phids[$binding->getPHID()])) {
|
||||
$free[] = $binding;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@title User Guide: Phabricator Clusters
|
||||
@group config
|
||||
|
||||
Guide on scaling Phabricator across multiple machines, for large installs.
|
||||
Guide on scaling Phabricator across multiple machines.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
@ -9,10 +9,42 @@ Overview
|
|||
IMPORTANT: Phabricator clustering is in its infancy and does not work at all
|
||||
yet. This document is mostly a placeholder.
|
||||
|
||||
Locking Services
|
||||
================
|
||||
|
||||
Very briefly, you can set "Can Manage Cluster Services" to "No One" to lock
|
||||
the cluster definition.
|
||||
IMPORTANT: DO NOT CONFIGURE CLUSTER SERVICES UNLESS YOU HAVE **TWENTY YEARS OF
|
||||
EXPERIENCE WITH PHABRICATOR** AND **A MINIMUM OF 17 PHABRICATOR PHDs**. YOU
|
||||
WILL BREAK YOUR INSTALL AND BE UNABLE TO REPAIR IT.
|
||||
|
||||
See also @{article:Almanac User Guide}.
|
||||
|
||||
|
||||
Managing Cluster Configuration
|
||||
==============================
|
||||
|
||||
Cluster configuration is managed primarily from the **Almanac** application.
|
||||
|
||||
To define cluster services and create or edit cluster configuration, you must
|
||||
have the **Can Manage Cluster Services** application permission in Almanac. If
|
||||
you do not have this permission, all cluster services and all connected devices
|
||||
will be locked and not editable.
|
||||
|
||||
The **Can Manage Cluster Services** permission is stronger than service and
|
||||
device policies, and overrides them. You can never edit a cluster service if
|
||||
you don't have this permission, even if the **Can Edit** policy on the service
|
||||
itself is very permissive.
|
||||
|
||||
|
||||
Locking Cluster Configuration
|
||||
=============================
|
||||
|
||||
IMPORTANT: Managing cluster services is **dangerous** and **fragile**.
|
||||
|
||||
If you make a mistake, you can break your install. Because the install is
|
||||
broken, you will be unable to load the web interface in order to repair it.
|
||||
|
||||
IMPORTANT: Currently, broken clusters must be repaired by manually fixing them
|
||||
in the database. There are no instructions available on how to do this, and no
|
||||
tools to help you. Do not configure cluster services.
|
||||
|
||||
If an attacker gains access to an account with permission to manage cluster
|
||||
services, they can add devices they control as database servers. These servers
|
||||
will then receive sensitive data and traffic, and allow the attacker to
|
||||
escalate their access and completely compromise an install.
|
||||
|
|
Loading…
Reference in a new issue