Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14017366
D15324.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
53 KB
Referenced Files
None
Subscribers
None
D15324.diff
View Options
diff --git a/resources/sql/autopatches/20160221.almanac.7.namespacen.sql b/resources/sql/autopatches/20160221.almanac.7.namespacen.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160221.almanac.7.namespacen.sql
@@ -0,0 +1,7 @@
+CREATE TABLE {$NAMESPACE}_almanac.almanac_namespacename_ngrams (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ objectID INT UNSIGNED NOT NULL,
+ ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
+ KEY `key_object` (objectID),
+ KEY `key_ngram` (ngram, objectID)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20160221.almanac.8.namespace.sql b/resources/sql/autopatches/20160221.almanac.8.namespace.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160221.almanac.8.namespace.sql
@@ -0,0 +1,14 @@
+CREATE TABLE {$NAMESPACE}_almanac.almanac_namespace (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ name VARCHAR(128) NOT NULL COLLATE {$COLLATE_TEXT},
+ nameIndex BINARY(12) NOT NULL,
+ mailKey BINARY(20) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid),
+ UNIQUE KEY `key_nameindex` (nameIndex),
+ KEY `key_name` (name)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/resources/sql/autopatches/20160221.almanac.9.namespacex.sql b/resources/sql/autopatches/20160221.almanac.9.namespacex.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160221.almanac.9.namespacex.sql
@@ -0,0 +1,19 @@
+CREATE TABLE {$NAMESPACE}_almanac.almanac_namespacetransaction (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ authorPHID VARBINARY(64) NOT NULL,
+ objectPHID VARBINARY(64) NOT NULL,
+ viewPolicy VARBINARY(64) NOT NULL,
+ editPolicy VARBINARY(64) NOT NULL,
+ commentPHID VARBINARY(64) DEFAULT NULL,
+ commentVersion INT UNSIGNED NOT NULL,
+ transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
+ oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (`phid`),
+ KEY `key_object` (`objectPHID`)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
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
@@ -28,6 +28,7 @@
'AlmanacCoreCustomField' => 'applications/almanac/customfield/AlmanacCoreCustomField.php',
'AlmanacCreateClusterServicesCapability' => 'applications/almanac/capability/AlmanacCreateClusterServicesCapability.php',
'AlmanacCreateDevicesCapability' => 'applications/almanac/capability/AlmanacCreateDevicesCapability.php',
+ 'AlmanacCreateNamespacesCapability' => 'applications/almanac/capability/AlmanacCreateNamespacesCapability.php',
'AlmanacCreateNetworksCapability' => 'applications/almanac/capability/AlmanacCreateNetworksCapability.php',
'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php',
'AlmanacCustomField' => 'applications/almanac/customfield/AlmanacCustomField.php',
@@ -61,6 +62,19 @@
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
'AlmanacNamesTestCase' => 'applications/almanac/util/__tests__/AlmanacNamesTestCase.php',
+ 'AlmanacNamespace' => 'applications/almanac/storage/AlmanacNamespace.php',
+ 'AlmanacNamespaceController' => 'applications/almanac/controller/AlmanacNamespaceController.php',
+ 'AlmanacNamespaceEditController' => 'applications/almanac/controller/AlmanacNamespaceEditController.php',
+ 'AlmanacNamespaceEditEngine' => 'applications/almanac/editor/AlmanacNamespaceEditEngine.php',
+ 'AlmanacNamespaceEditor' => 'applications/almanac/editor/AlmanacNamespaceEditor.php',
+ 'AlmanacNamespaceListController' => 'applications/almanac/controller/AlmanacNamespaceListController.php',
+ 'AlmanacNamespaceNameNgrams' => 'applications/almanac/storage/AlmanacNamespaceNameNgrams.php',
+ 'AlmanacNamespacePHIDType' => 'applications/almanac/phid/AlmanacNamespacePHIDType.php',
+ 'AlmanacNamespaceQuery' => 'applications/almanac/query/AlmanacNamespaceQuery.php',
+ 'AlmanacNamespaceSearchEngine' => 'applications/almanac/query/AlmanacNamespaceSearchEngine.php',
+ 'AlmanacNamespaceTransaction' => 'applications/almanac/storage/AlmanacNamespaceTransaction.php',
+ 'AlmanacNamespaceTransactionQuery' => 'applications/almanac/query/AlmanacNamespaceTransactionQuery.php',
+ 'AlmanacNamespaceViewController' => 'applications/almanac/controller/AlmanacNamespaceViewController.php',
'AlmanacNetwork' => 'applications/almanac/storage/AlmanacNetwork.php',
'AlmanacNetworkController' => 'applications/almanac/controller/AlmanacNetworkController.php',
'AlmanacNetworkEditController' => 'applications/almanac/controller/AlmanacNetworkEditController.php',
@@ -4000,6 +4014,7 @@
),
'AlmanacCreateClusterServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateDevicesCapability' => 'PhabricatorPolicyCapability',
+ 'AlmanacCreateNamespacesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateNetworksCapability' => 'PhabricatorPolicyCapability',
'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacCustomField' => 'PhabricatorCustomField',
@@ -4047,6 +4062,28 @@
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
'AlmanacNames' => 'Phobject',
'AlmanacNamesTestCase' => 'PhabricatorTestCase',
+ 'AlmanacNamespace' => array(
+ 'AlmanacDAO',
+ 'PhabricatorPolicyInterface',
+ 'PhabricatorCustomFieldInterface',
+ 'PhabricatorApplicationTransactionInterface',
+ 'PhabricatorProjectInterface',
+ 'AlmanacPropertyInterface',
+ 'PhabricatorDestructibleInterface',
+ 'PhabricatorNgramsInterface',
+ ),
+ 'AlmanacNamespaceController' => 'AlmanacController',
+ 'AlmanacNamespaceEditController' => 'AlmanacController',
+ 'AlmanacNamespaceEditEngine' => 'PhabricatorEditEngine',
+ 'AlmanacNamespaceEditor' => 'PhabricatorApplicationTransactionEditor',
+ 'AlmanacNamespaceListController' => 'AlmanacNamespaceController',
+ 'AlmanacNamespaceNameNgrams' => 'PhabricatorSearchNgrams',
+ 'AlmanacNamespacePHIDType' => 'PhabricatorPHIDType',
+ 'AlmanacNamespaceQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'AlmanacNamespaceSearchEngine' => 'PhabricatorApplicationSearchEngine',
+ 'AlmanacNamespaceTransaction' => 'PhabricatorApplicationTransaction',
+ 'AlmanacNamespaceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
+ 'AlmanacNamespaceViewController' => 'AlmanacNamespaceController',
'AlmanacNetwork' => array(
'AlmanacDAO',
'PhabricatorApplicationTransactionInterface',
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
@@ -29,7 +29,7 @@
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
return array(
array(
- 'name' => pht('Alamanac User Guide'),
+ 'name' => pht('Almanac User Guide'),
'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
),
);
@@ -44,12 +44,12 @@
'/almanac/' => array(
'' => 'AlmanacConsoleController',
'service/' => array(
- '(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacServiceListController',
+ $this->getQueryRoutePattern() => 'AlmanacServiceListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
),
'device/' => array(
- '(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacDeviceListController',
+ $this->getQueryRoutePattern() => 'AlmanacDeviceListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
),
@@ -61,7 +61,7 @@
'(?P<id>\d+)/' => 'AlmanacBindingViewController',
),
'network/' => array(
- '(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
+ $this->getQueryRoutePattern() => 'AlmanacNetworkListController',
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
'(?P<id>\d+)/' => 'AlmanacNetworkViewController',
),
@@ -69,6 +69,12 @@
'edit/' => 'AlmanacPropertyEditController',
'delete/' => 'AlmanacPropertyDeleteController',
),
+ 'namespace/' => array(
+ $this->getQueryRoutePattern() => 'AlmanacNamespaceListController',
+ $this->getEditRoutePattern('edit/')
+ => 'AlmanacNamespaceEditController',
+ '(?P<id>\d+)/' => 'AlmanacNamespaceViewController',
+ ),
),
);
}
@@ -84,6 +90,9 @@
AlmanacCreateNetworksCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
+ AlmanacCreateNamespacesCapability::CAPABILITY => array(
+ 'default' => PhabricatorPolicies::POLICY_ADMIN,
+ ),
AlmanacCreateClusterServicesCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
diff --git a/src/applications/almanac/capability/AlmanacCreateNamespacesCapability.php b/src/applications/almanac/capability/AlmanacCreateNamespacesCapability.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/capability/AlmanacCreateNamespacesCapability.php
@@ -0,0 +1,16 @@
+<?php
+
+final class AlmanacCreateNamespacesCapability
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'almanac.namespaces';
+
+ public function getCapabilityName() {
+ return pht('Can Create Namespaces');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to create Almanac namespaces.');
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacConsoleController.php b/src/applications/almanac/controller/AlmanacConsoleController.php
--- a/src/applications/almanac/controller/AlmanacConsoleController.php
+++ b/src/applications/almanac/controller/AlmanacConsoleController.php
@@ -14,24 +14,50 @@
$menu->addItem(
id(new PHUIObjectItemView())
- ->setHeader(pht('Services'))
- ->setHref($this->getApplicationURI('service/'))
- ->setIcon('fa-plug')
- ->addAttribute(pht('Manage Almanac services.')));
-
- $menu->addItem(
- id(new PHUIObjectItemView())
->setHeader(pht('Devices'))
->setHref($this->getApplicationURI('device/'))
->setIcon('fa-server')
- ->addAttribute(pht('Manage Almanac devices.')));
+ ->addAttribute(
+ pht(
+ 'Create an inventory of physical and virtual hosts and '.
+ 'devices.')));
+
+ $menu->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader(pht('Services'))
+ ->setHref($this->getApplicationURI('service/'))
+ ->setIcon('fa-plug')
+ ->addAttribute(
+ pht(
+ 'Create and update services, and map them to interfaces on '.
+ 'devices.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Networks'))
->setHref($this->getApplicationURI('network/'))
->setIcon('fa-globe')
- ->addAttribute(pht('Manage Almanac networks.')));
+ ->addAttribute(
+ pht(
+ 'Manage public and private networks.')));
+
+ $menu->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader(pht('Namespaces'))
+ ->setHref($this->getApplicationURI('namespace/'))
+ ->setIcon('fa-asterisk')
+ ->addAttribute(
+ pht('Control who can create new named services and devices.')));
+
+ $docs_uri = PhabricatorEnv::getDoclink(
+ 'Almanac User Guide');
+
+ $menu->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader(pht('Documentation'))
+ ->setHref($docs_uri)
+ ->setIcon('fa-book')
+ ->addAttribute(pht('Browse documentation for Almanac.')));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Console'));
diff --git a/src/applications/almanac/controller/AlmanacNamespaceController.php b/src/applications/almanac/controller/AlmanacNamespaceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacNamespaceController.php
@@ -0,0 +1,14 @@
+<?php
+
+abstract class AlmanacNamespaceController extends AlmanacController {
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ $list_uri = $this->getApplicationURI('namespace/');
+ $crumbs->addTextCrumb(pht('Namespaces'), $list_uri);
+
+ return $crumbs;
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacNamespaceEditController.php b/src/applications/almanac/controller/AlmanacNamespaceEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacNamespaceEditController.php
@@ -0,0 +1,11 @@
+<?php
+
+final class AlmanacNamespaceEditController extends AlmanacController {
+
+ public function handleRequest(AphrontRequest $request) {
+ return id(new AlmanacNamespaceEditEngine())
+ ->setController($this)
+ ->buildResponse();
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacNamespaceListController.php b/src/applications/almanac/controller/AlmanacNamespaceListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacNamespaceListController.php
@@ -0,0 +1,26 @@
+<?php
+
+final class AlmanacNamespaceListController
+ extends AlmanacNamespaceController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ return id(new AlmanacNamespaceSearchEngine())
+ ->setController($this)
+ ->buildResponse();
+ }
+
+ protected function buildApplicationCrumbs() {
+ $crumbs = parent::buildApplicationCrumbs();
+
+ id(new AlmanacNamespaceEditEngine())
+ ->setViewer($this->getViewer())
+ ->addActionToCrumbs($crumbs);
+
+ return $crumbs;
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacNamespaceViewController.php b/src/applications/almanac/controller/AlmanacNamespaceViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/controller/AlmanacNamespaceViewController.php
@@ -0,0 +1,87 @@
+<?php
+
+final class AlmanacNamespaceViewController
+ extends AlmanacNamespaceController {
+
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+
+ $id = $request->getURIData('id');
+ $namespace = id(new AlmanacNamespaceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$namespace) {
+ return new Aphront404Response();
+ }
+
+ $title = pht('Namespace %s', $namespace->getName());
+
+ $property_list = $this->buildPropertyList($namespace);
+ $action_list = $this->buildActionList($namespace);
+ $property_list->setActionList($action_list);
+
+ $header = id(new PHUIHeaderView())
+ ->setUser($viewer)
+ ->setHeader($namespace->getName())
+ ->setPolicyObject($namespace);
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($property_list);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addTextCrumb($namespace->getName());
+
+ $timeline = $this->buildTransactionTimeline(
+ $namespace,
+ new AlmanacNamespaceTransactionQuery());
+ $timeline->setShouldTerminate(true);
+
+ return $this->newPage()
+ ->setTitle($title)
+ ->setCrumbs($crumbs)
+ ->appendChild(
+ array(
+ $box,
+ $timeline,
+ ));
+ }
+
+ private function buildPropertyList(AlmanacNamespace $namespace) {
+ $viewer = $this->getViewer();
+
+ $properties = id(new PHUIPropertyListView())
+ ->setUser($viewer);
+
+ return $properties;
+ }
+
+ private function buildActionList(AlmanacNamespace $namespace) {
+ $viewer = $this->getViewer();
+ $id = $namespace->getID();
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $namespace,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $actions = id(new PhabricatorActionListView())
+ ->setUser($viewer);
+
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setIcon('fa-pencil')
+ ->setName(pht('Edit Namespace'))
+ ->setHref($this->getApplicationURI("namespace/edit/{$id}/"))
+ ->setWorkflow(!$can_edit)
+ ->setDisabled(!$can_edit));
+
+ return $actions;
+ }
+
+}
diff --git a/src/applications/almanac/controller/AlmanacPropertyEditController.php b/src/applications/almanac/controller/AlmanacPropertyEditController.php
--- a/src/applications/almanac/controller/AlmanacPropertyEditController.php
+++ b/src/applications/almanac/controller/AlmanacPropertyEditController.php
@@ -55,7 +55,7 @@
} else {
$caught = null;
try {
- AlmanacNames::validateServiceOrDeviceName($name);
+ AlmanacNames::validateName($name);
} catch (Exception $ex) {
$caught = $ex;
}
@@ -92,7 +92,7 @@
// Make sure property key is appropriate.
// TODO: It would be cleaner to put this safety check in the Editor.
- AlmanacNames::validateServiceOrDeviceName($property_key);
+ AlmanacNames::validateName($property_key);
// If we're adding a new property, put a placeholder on the object so
// that we can build a CustomField for it.
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
@@ -136,7 +136,7 @@
$name = $xaction->getNewValue();
try {
- AlmanacNames::validateServiceOrDeviceName($name);
+ AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
diff --git a/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php b/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/editor/AlmanacNamespaceEditEngine.php
@@ -0,0 +1,86 @@
+<?php
+
+final class AlmanacNamespaceEditEngine
+ extends PhabricatorEditEngine {
+
+ const ENGINECONST = 'almanac.namespace';
+
+ public function isEngineConfigurable() {
+ return false;
+ }
+
+ public function getEngineName() {
+ return pht('Almanac Namespaces');
+ }
+
+ public function getSummaryHeader() {
+ return pht('Edit Almanac Namespace Configurations');
+ }
+
+ public function getSummaryText() {
+ return pht('This engine is used to edit Almanac namespaces.');
+ }
+
+ public function getEngineApplicationClass() {
+ return 'PhabricatorAlmanacApplication';
+ }
+
+ protected function newEditableObject() {
+ return AlmanacNamespace::initializeNewNamespace();
+ }
+
+ protected function newObjectQuery() {
+ return new AlmanacNamespaceQuery();
+ }
+
+ protected function getObjectCreateTitleText($object) {
+ return pht('Create Namespace');
+ }
+
+ protected function getObjectCreateButtonText($object) {
+ return pht('Create Namespace');
+ }
+
+ protected function getObjectEditTitleText($object) {
+ return pht('Edit Namespace: %s', $object->getName());
+ }
+
+ protected function getObjectEditShortText($object) {
+ return pht('Edit Namespace');
+ }
+
+ protected function getObjectCreateShortText() {
+ return pht('Create Namespace');
+ }
+
+ protected function getEditorURI() {
+ return '/almanac/namespace/edit/';
+ }
+
+ protected function getObjectCreateCancelURI($object) {
+ return '/almanac/namespace/';
+ }
+
+ protected function getObjectViewURI($object) {
+ $id = $object->getID();
+ return "/almanac/namespace/{$id}/";
+ }
+
+ protected function getCreateNewObjectPolicy() {
+ return $this->getApplication()->getPolicy(
+ AlmanacCreateNamespacesCapability::CAPABILITY);
+ }
+
+ protected function buildCustomEditFields($object) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey('name')
+ ->setLabel(pht('Name'))
+ ->setDescription(pht('Name of the namespace.'))
+ ->setTransactionType(AlmanacNamespaceTransaction::TYPE_NAME)
+ ->setIsRequired(true)
+ ->setValue($object->getName()),
+ );
+ }
+
+}
diff --git a/src/applications/almanac/editor/AlmanacServiceEditor.php b/src/applications/almanac/editor/AlmanacNamespaceEditor.php
copy from src/applications/almanac/editor/AlmanacServiceEditor.php
copy to src/applications/almanac/editor/AlmanacNamespaceEditor.php
--- a/src/applications/almanac/editor/AlmanacServiceEditor.php
+++ b/src/applications/almanac/editor/AlmanacNamespaceEditor.php
@@ -1,6 +1,6 @@
<?php
-final class AlmanacServiceEditor
+final class AlmanacNamespaceEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
@@ -8,7 +8,7 @@
}
public function getEditorObjectsDescription() {
- return pht('Almanac Service');
+ return pht('Almanac Namespace');
}
protected function supportsSearch() {
@@ -18,9 +18,7 @@
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
- $types[] = AlmanacServiceTransaction::TYPE_NAME;
- $types[] = AlmanacServiceTransaction::TYPE_LOCK;
-
+ $types[] = AlmanacNamespaceTransaction::TYPE_NAME;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@@ -31,10 +29,8 @@
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
- case AlmanacServiceTransaction::TYPE_NAME:
+ case AlmanacNamespaceTransaction::TYPE_NAME:
return $object->getName();
- case AlmanacServiceTransaction::TYPE_LOCK:
- return (bool)$object->getIsLocked();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@@ -45,10 +41,8 @@
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
- case AlmanacServiceTransaction::TYPE_NAME:
+ case AlmanacNamespaceTransaction::TYPE_NAME:
return $xaction->getNewValue();
- case AlmanacServiceTransaction::TYPE_LOCK:
- return (bool)$xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
@@ -59,12 +53,9 @@
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
- case AlmanacServiceTransaction::TYPE_NAME:
+ case AlmanacNamespaceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
- case AlmanacServiceTransaction::TYPE_LOCK:
- $object->setIsLocked((int)$xaction->getNewValue());
- return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
@@ -75,24 +66,7 @@
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
- case AlmanacServiceTransaction::TYPE_NAME:
- return;
- case AlmanacServiceTransaction::TYPE_LOCK:
- $service = id(new AlmanacServiceQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs(array($object->getPHID()))
- ->needBindings(true)
- ->executeOne();
-
- $devices = array();
- foreach ($service->getBindings() as $binding) {
- $device = $binding->getInterface()->getDevice();
- $devices[$device->getPHID()] = $device;
- }
-
- foreach ($devices as $device) {
- $device->rebuildDeviceLocks();
- }
+ case AlmanacNamespaceTransaction::TYPE_NAME:
return;
}
@@ -107,7 +81,7 @@
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
- case AlmanacServiceTransaction::TYPE_NAME:
+ case AlmanacNamespaceTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
@@ -116,19 +90,18 @@
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
- pht('Service name is required.'),
+ pht('Namespace name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
} else {
foreach ($xactions as $xaction) {
- $message = null;
-
$name = $xaction->getNewValue();
+ $message = null;
try {
- AlmanacNames::validateServiceOrDeviceName($name);
+ AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
@@ -140,22 +113,25 @@
$message,
$xaction);
$errors[] = $error;
+ continue;
}
- }
- }
- if ($xactions) {
- $duplicate = id(new AlmanacServiceQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withNames(array(last($xactions)->getNewValue()))
- ->executeOne();
- if ($duplicate && ($duplicate->getID() != $object->getID())) {
- $error = new PhabricatorApplicationTransactionValidationError(
- $type,
- pht('Not Unique'),
- pht('Almanac services must have unique names.'),
- last($xactions));
- $errors[] = $error;
+ $other = id(new AlmanacNamespaceQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withNames(array($name))
+ ->executeOne();
+ if ($other && ($other->getID() != $object->getID())) {
+ $error = new PhabricatorApplicationTransactionValidationError(
+ $type,
+ pht('Invalid'),
+ pht(
+ 'The namespace name "%s" is already in use by another '.
+ 'namespace. Each namespace must have a unique name.',
+ $name),
+ $xaction);
+ $errors[] = $error;
+ continue;
+ }
}
}
@@ -165,6 +141,22 @@
return $errors;
}
+ protected function didCatchDuplicateKeyException(
+ PhabricatorLiskDAO $object,
+ array $xactions,
+ Exception $ex) {
+
+ $errors = array();
+ $errors[] = new PhabricatorApplicationTransactionValidationError(
+ null,
+ pht('Invalid'),
+ pht(
+ 'Another namespace with this name already exists. Each namespace '.
+ 'must have a unique name.'),
+ null);
+
+ throw new PhabricatorApplicationTransactionValidationException($errors);
+ }
}
diff --git a/src/applications/almanac/editor/AlmanacServiceEditor.php b/src/applications/almanac/editor/AlmanacServiceEditor.php
--- a/src/applications/almanac/editor/AlmanacServiceEditor.php
+++ b/src/applications/almanac/editor/AlmanacServiceEditor.php
@@ -128,7 +128,7 @@
$name = $xaction->getNewValue();
try {
- AlmanacNames::validateServiceOrDeviceName($name);
+ AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
diff --git a/src/applications/almanac/phid/AlmanacNamespacePHIDType.php b/src/applications/almanac/phid/AlmanacNamespacePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/phid/AlmanacNamespacePHIDType.php
@@ -0,0 +1,44 @@
+<?php
+
+final class AlmanacNamespacePHIDType extends PhabricatorPHIDType {
+
+ const TYPECONST = 'ANAM';
+
+ public function getTypeName() {
+ return pht('Almanac Namespace');
+ }
+
+ public function newObject() {
+ return new AlmanacNamespace();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorAlmanacApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new AlmanacNamespaceQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $namespace = $objects[$phid];
+
+ $id = $namespace->getID();
+ $name = $namespace->getName();
+
+ $handle->setObjectName(pht('Namespace %d', $id));
+ $handle->setName($name);
+ $handle->setURI($namespace->getURI());
+ }
+ }
+
+}
diff --git a/src/applications/almanac/query/AlmanacNamespaceQuery.php b/src/applications/almanac/query/AlmanacNamespaceQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/query/AlmanacNamespaceQuery.php
@@ -0,0 +1,103 @@
+<?php
+
+final class AlmanacNamespaceQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $names;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withNames(array $names) {
+ $this->names = $names;
+ return $this;
+ }
+
+ public function withNameNgrams($ngrams) {
+ return $this->withNgramsConstraint(
+ new AlmanacNamespaceNameNgrams(),
+ $ngrams);
+ }
+
+ public function newResultObject() {
+ return new AlmanacNamespace();
+ }
+
+ protected function loadPage() {
+ return $this->loadStandardPage($this->newResultObject());
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'namespace.id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'namespace.phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->names !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'namespace.name IN (%Ls)',
+ $this->names);
+ }
+
+ return $where;
+ }
+
+ protected function getPrimaryTableAlias() {
+ return 'namespace';
+ }
+
+ public function getOrderableColumns() {
+ return parent::getOrderableColumns() + array(
+ 'name' => array(
+ 'table' => $this->getPrimaryTableAlias(),
+ 'column' => 'name',
+ 'type' => 'string',
+ 'unique' => true,
+ 'reverse' => true,
+ ),
+ );
+ }
+
+ protected function getPagingValueMap($cursor, array $keys) {
+ $namespace = $this->loadCursorObject($cursor);
+ return array(
+ 'id' => $namespace->getID(),
+ 'name' => $namespace->getName(),
+ );
+ }
+
+ public function getBuiltinOrders() {
+ return array(
+ 'name' => array(
+ 'vector' => array('name'),
+ 'name' => pht('Namespace Name'),
+ ),
+ ) + parent::getBuiltinOrders();
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorAlmanacApplication';
+ }
+
+}
diff --git a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php b/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php
copy from src/applications/almanac/query/AlmanacNetworkSearchEngine.php
copy to src/applications/almanac/query/AlmanacNamespaceSearchEngine.php
--- a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php
+++ b/src/applications/almanac/query/AlmanacNamespaceSearchEngine.php
@@ -1,10 +1,10 @@
<?php
-final class AlmanacNetworkSearchEngine
+final class AlmanacNamespaceSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
- return pht('Almanac Networks');
+ return pht('Almanac Namespaces');
}
public function getApplicationClassName() {
@@ -12,7 +12,7 @@
}
public function newQuery() {
- return new AlmanacNetworkQuery();
+ return new AlmanacNamespaceQuery();
}
protected function buildCustomSearchFields() {
@@ -20,7 +20,7 @@
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
- ->setDescription(pht('Search for devices by name substring.')),
+ ->setDescription(pht('Search for namespaces by name substring.')),
);
}
@@ -35,12 +35,12 @@
}
protected function getURI($path) {
- return '/almanac/network/'.$path;
+ return '/almanac/namespace/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
- 'all' => pht('All Networks'),
+ 'all' => pht('All Namespaces'),
);
return $names;
@@ -60,30 +60,30 @@
}
protected function renderResultList(
- array $networks,
+ array $namespaces,
PhabricatorSavedQuery $query,
array $handles) {
- assert_instances_of($networks, 'AlmanacNetwork');
+ assert_instances_of($namespaces, 'AlmanacNamespace');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
- foreach ($networks as $network) {
- $id = $network->getID();
+ foreach ($namespaces as $namespace) {
+ $id = $namespace->getID();
$item = id(new PHUIObjectItemView())
- ->setObjectName(pht('Network %d', $id))
- ->setHeader($network->getName())
- ->setHref($this->getApplicationURI("network/{$id}/"))
- ->setObject($network);
+ ->setObjectName(pht('Namespace %d', $id))
+ ->setHeader($namespace->getName())
+ ->setHref($this->getApplicationURI("namespace/{$id}/"))
+ ->setObject($namespace);
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
- $result->setNoDataString(pht('No Almanac Networks found.'));
+ $result->setNoDataString(pht('No Almanac namespaces found.'));
return $result;
}
diff --git a/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php b/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/query/AlmanacNamespaceTransactionQuery.php
@@ -0,0 +1,10 @@
+<?php
+
+final class AlmanacNamespaceTransactionQuery
+ extends PhabricatorApplicationTransactionQuery {
+
+ public function getTemplateApplicationTransaction() {
+ return new AlmanacNamespaceTransaction();
+ }
+
+}
diff --git a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php
--- a/src/applications/almanac/query/AlmanacNetworkSearchEngine.php
+++ b/src/applications/almanac/query/AlmanacNetworkSearchEngine.php
@@ -20,7 +20,7 @@
id(new PhabricatorSearchTextField())
->setLabel(pht('Name Contains'))
->setKey('match')
- ->setDescription(pht('Search for devices by name substring.')),
+ ->setDescription(pht('Search for networks by name substring.')),
);
}
diff --git a/src/applications/almanac/storage/AlmanacDevice.php b/src/applications/almanac/storage/AlmanacDevice.php
--- a/src/applications/almanac/storage/AlmanacDevice.php
+++ b/src/applications/almanac/storage/AlmanacDevice.php
@@ -56,7 +56,7 @@
}
public function save() {
- AlmanacNames::validateServiceOrDeviceName($this->getName());
+ AlmanacNames::validateName($this->getName());
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
diff --git a/src/applications/almanac/storage/AlmanacService.php b/src/applications/almanac/storage/AlmanacNamespace.php
copy from src/applications/almanac/storage/AlmanacService.php
copy to src/applications/almanac/storage/AlmanacNamespace.php
--- a/src/applications/almanac/storage/AlmanacService.php
+++ b/src/applications/almanac/storage/AlmanacNamespace.php
@@ -1,6 +1,6 @@
<?php
-final class AlmanacService
+final class AlmanacNamespace
extends AlmanacDAO
implements
PhabricatorPolicyInterface,
@@ -16,20 +16,15 @@
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
- protected $serviceClass;
- protected $isLocked;
private $customFields = self::ATTACHABLE;
private $almanacProperties = self::ATTACHABLE;
- private $bindings = self::ATTACHABLE;
- private $serviceType = self::ATTACHABLE;
- public static function initializeNewService() {
- return id(new AlmanacService())
+ public static function initializeNewNamespace() {
+ return id(new self())
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
- ->attachAlmanacProperties(array())
- ->setIsLocked(0);
+ ->attachAlmanacProperties(array());
}
protected function getConfiguration() {
@@ -39,30 +34,26 @@
'name' => 'text128',
'nameIndex' => 'bytes12',
'mailKey' => 'bytes20',
- 'serviceClass' => 'text64',
- 'isLocked' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
- 'key_name' => array(
+ 'key_nameindex' => array(
'columns' => array('nameIndex'),
'unique' => true,
),
- 'key_nametext' => array(
+ 'key_name' => array(
'columns' => array('name'),
),
- 'key_class' => array(
- 'columns' => array('serviceClass'),
- ),
),
) + parent::getConfiguration();
}
public function generatePHID() {
- return PhabricatorPHID::generateNewPHID(AlmanacServicePHIDType::TYPECONST);
+ return PhabricatorPHID::generateNewPHID(
+ AlmanacNamespacePHIDType::TYPECONST);
}
public function save() {
- AlmanacNames::validateServiceOrDeviceName($this->getName());
+ AlmanacNames::validateName($this->getName());
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
@@ -74,25 +65,7 @@
}
public function getURI() {
- return '/almanac/service/view/'.$this->getName().'/';
- }
-
- public function getBindings() {
- return $this->assertAttached($this->bindings);
- }
-
- public function attachBindings(array $bindings) {
- $this->bindings = $bindings;
- return $this;
- }
-
- public function getServiceType() {
- return $this->assertAttached($this->serviceType);
- }
-
- public function attachServiceType(AlmanacServiceType $type) {
- $this->serviceType = $type;
- return $this;
+ return '/almanac/namespace/view/'.$this->getName().'/';
}
@@ -127,7 +100,7 @@
}
public function getAlmanacPropertyFieldSpecifications() {
- return $this->getServiceType()->getFieldSpecifications();
+ return array();
}
@@ -146,11 +119,7 @@
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
- if ($this->getIsLocked()) {
- return PhabricatorPolicies::POLICY_NOONE;
- } else {
- return $this->getEditPolicy();
- }
+ return $this->getEditPolicy();
}
}
@@ -159,14 +128,6 @@
}
public function describeAutomaticCapability($capability) {
- switch ($capability) {
- case PhabricatorPolicyCapability::CAN_EDIT:
- if ($this->getIsLocked()) {
- return pht('This service is locked and can not be edited.');
- }
- break;
- }
-
return null;
}
@@ -196,7 +157,7 @@
public function getApplicationTransactionEditor() {
- return new AlmanacServiceEditor();
+ return new AlmanacNamespaceEditor();
}
public function getApplicationTransactionObject() {
@@ -204,13 +165,12 @@
}
public function getApplicationTransactionTemplate() {
- return new AlmanacServiceTransaction();
+ return new AlmanacNamespaceTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
-
return $timeline;
}
@@ -220,15 +180,6 @@
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
-
- $bindings = id(new AlmanacBindingQuery())
- ->setViewer($engine->getViewer())
- ->withServicePHIDs(array($this->getPHID()))
- ->execute();
- foreach ($bindings as $binding) {
- $engine->destroyObject($binding);
- }
-
$this->delete();
}
@@ -238,7 +189,7 @@
public function newNgrams() {
return array(
- id(new AlmanacServiceNameNgrams())
+ id(new AlmanacNamespaceNameNgrams())
->setValue($this->getName()),
);
}
diff --git a/src/applications/almanac/storage/AlmanacNamespaceNameNgrams.php b/src/applications/almanac/storage/AlmanacNamespaceNameNgrams.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/storage/AlmanacNamespaceNameNgrams.php
@@ -0,0 +1,18 @@
+<?php
+
+final class AlmanacNamespaceNameNgrams
+ extends PhabricatorSearchNgrams {
+
+ public function getNgramKey() {
+ return 'namespacename';
+ }
+
+ public function getColumnName() {
+ return 'name';
+ }
+
+ public function getApplicationName() {
+ return 'almanac';
+ }
+
+}
diff --git a/src/applications/almanac/storage/AlmanacNamespaceTransaction.php b/src/applications/almanac/storage/AlmanacNamespaceTransaction.php
new file mode 100644
--- /dev/null
+++ b/src/applications/almanac/storage/AlmanacNamespaceTransaction.php
@@ -0,0 +1,43 @@
+<?php
+
+final class AlmanacNamespaceTransaction
+ extends PhabricatorApplicationTransaction {
+
+ const TYPE_NAME = 'almanac:namespace:name';
+
+ public function getApplicationName() {
+ return 'almanac';
+ }
+
+ public function getApplicationTransactionType() {
+ return AlmanacNamespacePHIDType::TYPECONST;
+ }
+
+ public function getApplicationTransactionCommentObject() {
+ return null;
+ }
+
+ public function getTitle() {
+ $author_phid = $this->getAuthorPHID();
+
+ $old = $this->getOldValue();
+ $new = $this->getNewValue();
+
+ switch ($this->getTransactionType()) {
+ case PhabricatorTransactions::TYPE_CREATE:
+ return pht(
+ '%s created this namespace.',
+ $this->renderHandleLink($author_phid));
+ break;
+ case self::TYPE_NAME:
+ return pht(
+ '%s renamed this namespace from "%s" to "%s".',
+ $this->renderHandleLink($author_phid),
+ $old,
+ $new);
+ }
+
+ return parent::getTitle();
+ }
+
+}
diff --git a/src/applications/almanac/storage/AlmanacService.php b/src/applications/almanac/storage/AlmanacService.php
--- a/src/applications/almanac/storage/AlmanacService.php
+++ b/src/applications/almanac/storage/AlmanacService.php
@@ -62,7 +62,7 @@
}
public function save() {
- AlmanacNames::validateServiceOrDeviceName($this->getName());
+ AlmanacNames::validateName($this->getName());
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
diff --git a/src/applications/almanac/util/AlmanacNames.php b/src/applications/almanac/util/AlmanacNames.php
--- a/src/applications/almanac/util/AlmanacNames.php
+++ b/src/applications/almanac/util/AlmanacNames.php
@@ -2,54 +2,61 @@
final class AlmanacNames extends Phobject {
- public static function validateServiceOrDeviceName($name) {
+ public static function validateName($name) {
if (strlen($name) < 3) {
throw new Exception(
pht(
- 'Almanac service and device names must be at least 3 '.
- 'characters long.'));
+ 'Almanac service, device, property and namespace names must be '.
+ 'at least 3 characters long.'));
+ }
+
+ if (strlen($name) > 100) {
+ throw new Exception(
+ pht(
+ 'Almanac service, device, property and namespace names may not '.
+ 'be more than 100 characters long.'));
}
if (!preg_match('/^[a-z0-9.-]+\z/', $name)) {
throw new Exception(
pht(
- 'Almanac service and device names may only contain lowercase '.
- 'letters, numbers, hyphens, and periods.'));
+ 'Almanac service, device, property and namespace names may only '.
+ 'contain lowercase letters, numbers, hyphens, and periods.'));
}
if (preg_match('/(^|\\.)\d+(\z|\\.)/', $name)) {
throw new Exception(
pht(
- 'Almanac service and device names may not have any segments '.
- 'containing only digits.'));
+ 'Almanac service, device, property and namespace names may not '.
+ 'have any segments containing only digits.'));
}
if (preg_match('/\.\./', $name)) {
throw new Exception(
pht(
- 'Almanac service and device names may not contain multiple '.
- 'consecutive periods.'));
+ 'Almanac service, device, property and namespace names may not '.
+ 'contain multiple consecutive periods.'));
}
if (preg_match('/\\.-|-\\./', $name)) {
throw new Exception(
pht(
- 'Amanac service and device names may not contain hyphens adjacent '.
- 'to periods.'));
+ 'Almanac service, device, property and namespace names may not '.
+ 'contain hyphens adjacent to periods.'));
}
if (preg_match('/--/', $name)) {
throw new Exception(
pht(
- 'Almanac service and device names may not contain multiple '.
- 'consecutive hyphens.'));
+ 'Almanac service, device, property and namespace names may not '.
+ 'contain multiple consecutive hyphens.'));
}
if (!preg_match('/^[a-z0-9].*[a-z0-9]\z/', $name)) {
throw new Exception(
pht(
- 'Almanac service and device names must begin and end with a letter '.
- 'or number.'));
+ 'Almanac service, device, property and namespace names must begin '.
+ 'and end with a letter or number.'));
}
}
diff --git a/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php b/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php
--- a/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php
+++ b/src/applications/almanac/util/__tests__/AlmanacNamesTestCase.php
@@ -33,12 +33,16 @@
'db.phacility.instance' => true,
'web002.useast.example.com' => true,
'master.example-corp.com' => true,
+
+ // Maximum length is 100.
+ str_repeat('a', 100) => true,
+ str_repeat('a', 101) => false,
);
foreach ($map as $input => $expect) {
$caught = null;
try {
- AlmanacNames::validateServiceOrDeviceName($input);
+ AlmanacNames::validateName($input);
} catch (Exception $ex) {
$caught = $ex;
}
diff --git a/src/docs/user/userguide/almanac.diviner b/src/docs/user/userguide/almanac.diviner
--- a/src/docs/user/userguide/almanac.diviner
+++ b/src/docs/user/userguide/almanac.diviner
@@ -1,13 +1,138 @@
@title Almanac User Guide
@group userguide
-Using Almanac to manage services.
+Using Almanac to manage devices and services.
-= Overview =
+Overview
+========
IMPORTANT: Almanac is a prototype application. See
@{article:User Guide: Prototype Applications}.
+Almanac is a device and service inventory application. It allows you to create
+lists of //devices// and //services// that humans and other applications can
+use to keep track of what is running where.
+
+At a very high level, Almanac can be thought of as a bit like a DNS server.
+Callers ask it for information about services, and it responds with details
+about which devices host those services. However, it can respond to a broader
+range of queries and provide more detailed responses than DNS alone can.
+
+Today, the primary use cases for Almanac involve configuring Phabricator
+itself: Almanac is used to configure Phabricator to operate in a cluster setup,
+and to expose hardware to Drydock so it can run build and integration tasks.
+
+Beyond internal uses, Almanac is a general-purpose service and device inventory
+application and can be used to configure and manage other types of service and
+hardware inventories, but these use cases are currently considered experimental
+and you should be exercise caution in pursuing them.
+
+
+Example: Drydock Build Pool
+================================
+
+Here's a quick example of how you might configure Almanac to solve a real-world
+problem. This section describes configuration at a high level to give you an
+introduction to Almanac concepts and a better idea of how the pieces fit
+together.
+
+In this scenario, we want to use Drydock to run some sort of build process. To
+do this, Drydock needs hardware to run on. We're going to use Almanac to tell
+Drydock about the hardware it should use.
+
+In this scenario, Almanac will work a bit like a DNS server. When we're done,
+Drydock will be able to query Almanac for information about a service (like
+`build.mycompany.com`) and get back information about which hosts are part of
+that service and where it should connect to.
+
+Before getting started, we need to create a **network**. For simplicity, let's
+suppose everything will be connected through the public internet. If you
+haven't already, you'd create a "Public Internet" network first.
+
+Once we have a network, we create the actual physical or virtual hosts by
+launching instances in EC2, or racking and powering on some servers, or already
+having some hardware on hand we want to use. We set the hosts up normally and
+connect them to the internet or network.
+
+After the hosts exist, we add them to Almanac as **devices**, like
+`build001.mycompany.com`, `build002.mycompany.com`, and so on. In Almanac,
+devices are usually physical or virtual hosts, although you could also use it
+to inventory other types of devices and hardware.
+
+For each **device**, we add an **interface**. This is just an address and port
+on a particular network. Since we're going to connect to these hosts over
+SSH, we'll add interfaces on the standard SSH port 22. An example configuration
+might look a little bit like this:
+
+| Device | Network | Address | Port |
+|--------|---------|---------|------|
+| `build001.mycompany.com` | Public Internet | 58.8.9.10 | 22
+| `build002.mycompany.com` | Public Internet | 58.8.9.11 | 22
+| ... | Public Internet | ... | 22
+
+Now, we create the **service**. This is what we'll tell Drydock about, and
+it can query for information about this service to find connected devices.
+Here, we'll call it `build.mycompany.com`.
+
+After creating the service, add **bindings** to the interfaces we configured
+above. This will tell Drydock where it should actually connect to.
+
+Once this is complete, we're done in Almanac and can continue configuration in
+Drydock, which is outside the scope of this example. Once everything is fully
+configured, this is how Almanac will be used by Drydock:
+
+ - Drydock will query information about `build.mycompany.com` from Almanac.
+ - Drydock will get back a list of bound interfaces, among other data.
+ - The interfaces provide information about addresses and ports that Drydock
+ can use to connect to the actual devices.
+
+You can now add and remove devices to the pool by binding them and unbinding
+them from the service.
+
+
+Concepts
+========
+
+The major concepts in Almanac are **devices*, **interfaces**, **services**,
+**bindings**, **networks**, and **namespaces**.
+
+**Devices**: Almanac devices represent physical or virtual devices.
+Usually, they are hosts (like `web001.mycompany.net`), although you could
+use devices to keep inventory of any other kind of device or physical asset
+(like phones, laptops, or office chairs).
+
+Each device has a name, and may have properties and interfaces.
+
+**Interfaces**: Interfaces are listening address/port combinations on devices.
+For example, if you have a webserver host device named `web001.mycompany.net`,
+you might add an interface on port `80`.
+
+Interfaces tell users and applications where they should connect to to access
+services and devices.
+
+**Services**: These are named services like `build.mycompany.net` that work
+a bit like DNS. Humans or other applications can look up a service to find
+configuration information and learn which devices are hosting the service.
+
+Each service has a name, and may have properties and bindings.
+
+**Bindings**: Bindings are connections between services and interfaces. They
+tell callers which devices host a named service.
+
+**Networks**: Networks allow Almanac to distingiush between addresses on
+different networks, like VPNs vs the public internet.
+
+If you have hosts in different VPNs or on private networks, you might have
+multiple devices which share the same IP address (like `10.0.0.3`). Networks
+allow Almanac to distinguish between devices with the same address on different
+sections of the network.
+
+**Namespaces**: Namespaces let you control who is permitted to create devices
+and services with particular names. For example, the namespace `mycompany.com`
+controls who can create services with names like `a.mycompany.com` and
+`b.mycompany.com`.
+
+
Locking and Unlocking Services
==============================
@@ -17,8 +142,8 @@
For more details on this scenario, see
@{article:User Guide: Phabricator Clusters}.
-Beyond hardening cluster definitions, you might also want to lock a service to
-prevent accidental edits.
+Beyond hardening cluster definitions, you might also want to lock a critical
+service to prevent accidental edits.
To lock a service, run:
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 5, 4:55 PM (1 w, 5 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/5b/2d/zmylgxttq6cfqvxp
Default Alt Text
D15324.diff (53 KB)
Attached To
Mode
D15324: Rough-in Almanac namespaces
Attached
Detach File
Event Timeline
Log In to Comment