Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13994753
D15345.id37004.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
25 KB
Referenced Files
None
Subscribers
None
D15345.id37004.diff
View Options
diff --git a/resources/sql/autopatches/20160225.almanac.1.disablebinding.sql b/resources/sql/autopatches/20160225.almanac.1.disablebinding.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160225.almanac.1.disablebinding.sql
@@ -0,0 +1,2 @@
+ALTER TABLE {$NAMESPACE}_almanac.almanac_binding
+ ADD isDisabled BOOL NOT NULL;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -11,6 +11,7 @@
'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 @@
'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 @@
'PhabricatorDestructibleInterface',
'PhabricatorExtendedPolicyInterface',
),
+ 'AlmanacBindingDisableController' => 'AlmanacServiceController',
'AlmanacBindingEditController' => 'AlmanacServiceController',
'AlmanacBindingEditor' => 'AlmanacEditor',
'AlmanacBindingPHIDType' => 'PhabricatorPHIDType',
@@ -4049,8 +4052,10 @@
'AlmanacDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
+ 'PhabricatorExtendedPolicyInterface',
),
'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource',
+ 'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController',
'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
'AlmanacInterfaceQuery' => 'AlmanacQuery',
diff --git a/src/applications/almanac/application/PhabricatorAlmanacApplication.php b/src/applications/almanac/application/PhabricatorAlmanacApplication.php
--- a/src/applications/almanac/application/PhabricatorAlmanacApplication.php
+++ b/src/applications/almanac/application/PhabricatorAlmanacApplication.php
@@ -55,9 +55,11 @@
),
'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 @@
}
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 @@
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
AlmanacManageClusterServicesCapability::CAPABILITY => array(
- 'default' => PhabricatorPolicies::POLICY_ADMIN,
+ 'default' => PhabricatorPolicies::POLICY_NOONE,
+ 'caption' => $cluster_caption,
),
);
}
diff --git a/src/applications/almanac/controller/AlmanacBindingDisableController.php b/src/applications/almanac/controller/AlmanacBindingDisableController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacBindingDisableController.php
@@ -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);
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacBindingViewController.php b/src/applications/almanac/controller/AlmanacBindingViewController.php
--- a/src/applications/almanac/controller/AlmanacBindingViewController.php
+++ b/src/applications/almanac/controller/AlmanacBindingViewController.php
@@ -35,6 +35,10 @@
->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 @@
->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;
}
diff --git a/src/applications/almanac/controller/AlmanacController.php b/src/applications/almanac/controller/AlmanacController.php
--- a/src/applications/almanac/controller/AlmanacController.php
+++ b/src/applications/almanac/controller/AlmanacController.php
@@ -177,7 +177,8 @@
$doc_link = phutil_tag(
'a',
array(
- 'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
+ 'href' => PhabricatorEnv::getDoclink(
+ 'User Guide: Phabricator Clusters'),
'target' => '_blank',
),
pht('Learn More'));
diff --git a/src/applications/almanac/controller/AlmanacInterfaceDeleteController.php b/src/applications/almanac/controller/AlmanacInterfaceDeleteController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacInterfaceDeleteController.php
@@ -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'));
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacServiceViewController.php b/src/applications/almanac/controller/AlmanacServiceViewController.php
--- a/src/applications/almanac/controller/AlmanacServiceViewController.php
+++ b/src/applications/almanac/controller/AlmanacServiceViewController.php
@@ -122,7 +122,8 @@
->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'))
diff --git a/src/applications/almanac/editor/AlmanacBindingEditor.php b/src/applications/almanac/editor/AlmanacBindingEditor.php
--- a/src/applications/almanac/editor/AlmanacBindingEditor.php
+++ b/src/applications/almanac/editor/AlmanacBindingEditor.php
@@ -13,6 +13,7 @@
$types = parent::getTransactionTypes();
$types[] = AlmanacBindingTransaction::TYPE_INTERFACE;
+ $types[] = AlmanacBindingTransaction::TYPE_DISABLE;
return $types;
}
@@ -23,6 +24,8 @@
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 @@
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 @@
$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 @@
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
+ case AlmanacBindingTransaction::TYPE_DISABLE:
+ return;
case AlmanacBindingTransaction::TYPE_INTERFACE:
$interface_phids = array();
diff --git a/src/applications/almanac/editor/AlmanacDeviceEditor.php b/src/applications/almanac/editor/AlmanacDeviceEditor.php
--- a/src/applications/almanac/editor/AlmanacDeviceEditor.php
+++ b/src/applications/almanac/editor/AlmanacDeviceEditor.php
@@ -310,6 +310,19 @@
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;
+ }
}
}
}
diff --git a/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php b/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php
--- a/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php
+++ b/src/applications/almanac/engineextension/AlmanacSearchEngineAttachment.php
@@ -28,6 +28,7 @@
'phid' => $binding->getPHID(),
'properties' => $this->getAlmanacPropertyList($binding),
'interface' => $this->getAlmanacInterfaceDictionary($interface),
+ 'disabled' => (bool)$binding->getIsDisabled(),
);
}
diff --git a/src/applications/almanac/storage/AlmanacBinding.php b/src/applications/almanac/storage/AlmanacBinding.php
--- a/src/applications/almanac/storage/AlmanacBinding.php
+++ b/src/applications/almanac/storage/AlmanacBinding.php
@@ -13,6 +13,7 @@
protected $devicePHID;
protected $interfacePHID;
protected $mailKey;
+ protected $isDisabled;
private $service = self::ATTACHABLE;
private $device = self::ATTACHABLE;
@@ -22,7 +23,8 @@
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 @@
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'mailKey' => 'bytes20',
+ 'isDisabled' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_service' => array(
diff --git a/src/applications/almanac/storage/AlmanacBindingTransaction.php b/src/applications/almanac/storage/AlmanacBindingTransaction.php
--- a/src/applications/almanac/storage/AlmanacBindingTransaction.php
+++ b/src/applications/almanac/storage/AlmanacBindingTransaction.php
@@ -4,6 +4,7 @@
extends AlmanacTransaction {
const TYPE_INTERFACE = 'almanac:binding:interface';
+ const TYPE_DISABLE = 'almanac:binding:disable';
public function getApplicationName() {
return 'almanac';
@@ -57,6 +58,17 @@
$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();
diff --git a/src/applications/almanac/storage/AlmanacDeviceTransaction.php b/src/applications/almanac/storage/AlmanacDeviceTransaction.php
--- a/src/applications/almanac/storage/AlmanacDeviceTransaction.php
+++ b/src/applications/almanac/storage/AlmanacDeviceTransaction.php
@@ -69,7 +69,7 @@
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.',
diff --git a/src/applications/almanac/storage/AlmanacInterface.php b/src/applications/almanac/storage/AlmanacInterface.php
--- a/src/applications/almanac/storage/AlmanacInterface.php
+++ b/src/applications/almanac/storage/AlmanacInterface.php
@@ -4,7 +4,8 @@
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
- PhabricatorDestructibleInterface {
+ PhabricatorDestructibleInterface,
+ PhabricatorExtendedPolicyInterface {
protected $devicePHID;
protected $networkPHID;
@@ -74,6 +75,16 @@
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 @@
}
+/* -( 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 )----------------------------------- */
diff --git a/src/applications/almanac/view/AlmanacBindingTableView.php b/src/applications/almanac/view/AlmanacBindingTableView.php
--- a/src/applications/almanac/view/AlmanacBindingTableView.php
+++ b/src/applications/almanac/view/AlmanacBindingTableView.php
@@ -5,6 +5,8 @@
private $bindings;
private $noDataString;
+ private $hideServiceColumn;
+
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
@@ -23,6 +25,15 @@
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 @@
}
$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 @@
$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 @@
->setHeaders(
array(
pht('ID'),
+ null,
pht('Service'),
pht('Device'),
pht('Network'),
@@ -70,11 +99,18 @@
->setColumnClasses(
array(
'',
+ 'icon',
'',
'',
'',
'wide',
'action',
+ ))
+ ->setColumnVisibility(
+ array(
+ true,
+ true,
+ !$this->getHideServiceColumn(),
));
return $table;
diff --git a/src/applications/almanac/view/AlmanacInterfaceTableView.php b/src/applications/almanac/view/AlmanacInterfaceTableView.php
--- a/src/applications/almanac/view/AlmanacInterfaceTableView.php
+++ b/src/applications/almanac/view/AlmanacInterfaceTableView.php
@@ -27,7 +27,9 @@
$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 @@
$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 @@
pht('Address'),
pht('Port'),
null,
+ null,
))
->setColumnClasses(
array(
@@ -68,6 +80,7 @@
'',
'',
'action',
+ 'action',
));
return $table;
diff --git a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockAlmanacServiceHostBlueprintImplementation.php
@@ -267,6 +267,11 @@
$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;
}
diff --git a/src/docs/user/configuration/cluster.diviner b/src/docs/user/configuration/cluster.diviner
--- a/src/docs/user/configuration/cluster.diviner
+++ b/src/docs/user/configuration/cluster.diviner
@@ -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 @@
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.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Oct 24, 8:19 AM (3 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6738715
Default Alt Text
D15345.id37004.diff (25 KB)
Attached To
Mode
D15345: Allow Almanac bindings to be disabled and unused interfaces to be removed
Attached
Detach File
Event Timeline
Log In to Comment