mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Rough-in Almanac namespaces
Summary: Ref T6741. Ref T10246. Root problem: to provide Drydock in the cluster, we need to expose Almanac, and doing so would let users accidentally or intentionally create a bunch of `repo006.phacility.net` devices/services which could conflict with the real ones we manage. There's currently no way to say "you can't create anything named `*.blah.net`". This adds "namespaces", which let you do that (well, not yet, but they will after the next diff). After the next diff, if you try to create `repo003.phacility.net`, but the namespace `phacility.net` already exists and you don't have permission to edit it, you'll be asked to choose a different name. Also various modernizations and some new docs. Test Plan: - Created cool namespaces like `this.computer`. - Almanac namespaces don't actually enforce policies yet. Reviewers: chad Reviewed By: chad Maniphest Tasks: T6741, T10246 Differential Revision: https://secure.phabricator.com/D15324
This commit is contained in:
parent
50debecf52
commit
db50d0fb11
29 changed files with 1195 additions and 40 deletions
|
@ -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};
|
14
resources/sql/autopatches/20160221.almanac.8.namespace.sql
Normal file
14
resources/sql/autopatches/20160221.almanac.8.namespace.sql
Normal file
|
@ -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};
|
19
resources/sql/autopatches/20160221.almanac.9.namespacex.sql
Normal file
19
resources/sql/autopatches/20160221.almanac.9.namespacex.sql
Normal file
|
@ -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};
|
|
@ -28,6 +28,7 @@ phutil_register_library_map(array(
|
||||||
'AlmanacCoreCustomField' => 'applications/almanac/customfield/AlmanacCoreCustomField.php',
|
'AlmanacCoreCustomField' => 'applications/almanac/customfield/AlmanacCoreCustomField.php',
|
||||||
'AlmanacCreateClusterServicesCapability' => 'applications/almanac/capability/AlmanacCreateClusterServicesCapability.php',
|
'AlmanacCreateClusterServicesCapability' => 'applications/almanac/capability/AlmanacCreateClusterServicesCapability.php',
|
||||||
'AlmanacCreateDevicesCapability' => 'applications/almanac/capability/AlmanacCreateDevicesCapability.php',
|
'AlmanacCreateDevicesCapability' => 'applications/almanac/capability/AlmanacCreateDevicesCapability.php',
|
||||||
|
'AlmanacCreateNamespacesCapability' => 'applications/almanac/capability/AlmanacCreateNamespacesCapability.php',
|
||||||
'AlmanacCreateNetworksCapability' => 'applications/almanac/capability/AlmanacCreateNetworksCapability.php',
|
'AlmanacCreateNetworksCapability' => 'applications/almanac/capability/AlmanacCreateNetworksCapability.php',
|
||||||
'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php',
|
'AlmanacCreateServicesCapability' => 'applications/almanac/capability/AlmanacCreateServicesCapability.php',
|
||||||
'AlmanacCustomField' => 'applications/almanac/customfield/AlmanacCustomField.php',
|
'AlmanacCustomField' => 'applications/almanac/customfield/AlmanacCustomField.php',
|
||||||
|
@ -61,6 +62,19 @@ phutil_register_library_map(array(
|
||||||
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
|
'AlmanacManagementWorkflow' => 'applications/almanac/management/AlmanacManagementWorkflow.php',
|
||||||
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
|
'AlmanacNames' => 'applications/almanac/util/AlmanacNames.php',
|
||||||
'AlmanacNamesTestCase' => 'applications/almanac/util/__tests__/AlmanacNamesTestCase.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',
|
'AlmanacNetwork' => 'applications/almanac/storage/AlmanacNetwork.php',
|
||||||
'AlmanacNetworkController' => 'applications/almanac/controller/AlmanacNetworkController.php',
|
'AlmanacNetworkController' => 'applications/almanac/controller/AlmanacNetworkController.php',
|
||||||
'AlmanacNetworkEditController' => 'applications/almanac/controller/AlmanacNetworkEditController.php',
|
'AlmanacNetworkEditController' => 'applications/almanac/controller/AlmanacNetworkEditController.php',
|
||||||
|
@ -4000,6 +4014,7 @@ phutil_register_library_map(array(
|
||||||
),
|
),
|
||||||
'AlmanacCreateClusterServicesCapability' => 'PhabricatorPolicyCapability',
|
'AlmanacCreateClusterServicesCapability' => 'PhabricatorPolicyCapability',
|
||||||
'AlmanacCreateDevicesCapability' => 'PhabricatorPolicyCapability',
|
'AlmanacCreateDevicesCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'AlmanacCreateNamespacesCapability' => 'PhabricatorPolicyCapability',
|
||||||
'AlmanacCreateNetworksCapability' => 'PhabricatorPolicyCapability',
|
'AlmanacCreateNetworksCapability' => 'PhabricatorPolicyCapability',
|
||||||
'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability',
|
'AlmanacCreateServicesCapability' => 'PhabricatorPolicyCapability',
|
||||||
'AlmanacCustomField' => 'PhabricatorCustomField',
|
'AlmanacCustomField' => 'PhabricatorCustomField',
|
||||||
|
@ -4047,6 +4062,28 @@ phutil_register_library_map(array(
|
||||||
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'AlmanacManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
'AlmanacNames' => 'Phobject',
|
'AlmanacNames' => 'Phobject',
|
||||||
'AlmanacNamesTestCase' => 'PhabricatorTestCase',
|
'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(
|
'AlmanacNetwork' => array(
|
||||||
'AlmanacDAO',
|
'AlmanacDAO',
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
|
|
|
@ -29,7 +29,7 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
|
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
|
||||||
return array(
|
return array(
|
||||||
array(
|
array(
|
||||||
'name' => pht('Alamanac User Guide'),
|
'name' => pht('Almanac User Guide'),
|
||||||
'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
|
'href' => PhabricatorEnv::getDoclink('Almanac User Guide'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -44,12 +44,12 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
'/almanac/' => array(
|
'/almanac/' => array(
|
||||||
'' => 'AlmanacConsoleController',
|
'' => 'AlmanacConsoleController',
|
||||||
'service/' => array(
|
'service/' => array(
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacServiceListController',
|
$this->getQueryRoutePattern() => 'AlmanacServiceListController',
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController',
|
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacServiceEditController',
|
||||||
'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
|
'view/(?P<name>[^/]+)/' => 'AlmanacServiceViewController',
|
||||||
),
|
),
|
||||||
'device/' => array(
|
'device/' => array(
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacDeviceListController',
|
$this->getQueryRoutePattern() => 'AlmanacDeviceListController',
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
|
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacDeviceEditController',
|
||||||
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
|
'view/(?P<name>[^/]+)/' => 'AlmanacDeviceViewController',
|
||||||
),
|
),
|
||||||
|
@ -61,7 +61,7 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
'(?P<id>\d+)/' => 'AlmanacBindingViewController',
|
'(?P<id>\d+)/' => 'AlmanacBindingViewController',
|
||||||
),
|
),
|
||||||
'network/' => array(
|
'network/' => array(
|
||||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'AlmanacNetworkListController',
|
$this->getQueryRoutePattern() => 'AlmanacNetworkListController',
|
||||||
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
|
'edit/(?:(?P<id>\d+)/)?' => 'AlmanacNetworkEditController',
|
||||||
'(?P<id>\d+)/' => 'AlmanacNetworkViewController',
|
'(?P<id>\d+)/' => 'AlmanacNetworkViewController',
|
||||||
),
|
),
|
||||||
|
@ -69,6 +69,12 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
'edit/' => 'AlmanacPropertyEditController',
|
'edit/' => 'AlmanacPropertyEditController',
|
||||||
'delete/' => 'AlmanacPropertyDeleteController',
|
'delete/' => 'AlmanacPropertyDeleteController',
|
||||||
),
|
),
|
||||||
|
'namespace/' => array(
|
||||||
|
$this->getQueryRoutePattern() => 'AlmanacNamespaceListController',
|
||||||
|
$this->getEditRoutePattern('edit/')
|
||||||
|
=> 'AlmanacNamespaceEditController',
|
||||||
|
'(?P<id>\d+)/' => 'AlmanacNamespaceViewController',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,6 +90,9 @@ final class PhabricatorAlmanacApplication extends PhabricatorApplication {
|
||||||
AlmanacCreateNetworksCapability::CAPABILITY => array(
|
AlmanacCreateNetworksCapability::CAPABILITY => array(
|
||||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||||
),
|
),
|
||||||
|
AlmanacCreateNamespacesCapability::CAPABILITY => array(
|
||||||
|
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||||
|
),
|
||||||
AlmanacCreateClusterServicesCapability::CAPABILITY => array(
|
AlmanacCreateClusterServicesCapability::CAPABILITY => array(
|
||||||
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||||
),
|
),
|
||||||
|
|
|
@ -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.');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,26 +12,52 @@ final class AlmanacConsoleController extends AlmanacController {
|
||||||
$menu = id(new PHUIObjectItemListView())
|
$menu = id(new PHUIObjectItemListView())
|
||||||
->setUser($viewer);
|
->setUser($viewer);
|
||||||
|
|
||||||
$menu->addItem(
|
|
||||||
id(new PHUIObjectItemView())
|
|
||||||
->setHeader(pht('Services'))
|
|
||||||
->setHref($this->getApplicationURI('service/'))
|
|
||||||
->setIcon('fa-plug')
|
|
||||||
->addAttribute(pht('Manage Almanac services.')));
|
|
||||||
|
|
||||||
$menu->addItem(
|
$menu->addItem(
|
||||||
id(new PHUIObjectItemView())
|
id(new PHUIObjectItemView())
|
||||||
->setHeader(pht('Devices'))
|
->setHeader(pht('Devices'))
|
||||||
->setHref($this->getApplicationURI('device/'))
|
->setHref($this->getApplicationURI('device/'))
|
||||||
->setIcon('fa-server')
|
->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(
|
$menu->addItem(
|
||||||
id(new PHUIObjectItemView())
|
id(new PHUIObjectItemView())
|
||||||
->setHeader(pht('Networks'))
|
->setHeader(pht('Networks'))
|
||||||
->setHref($this->getApplicationURI('network/'))
|
->setHref($this->getApplicationURI('network/'))
|
||||||
->setIcon('fa-globe')
|
->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 = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb(pht('Console'));
|
$crumbs->addTextCrumb(pht('Console'));
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class AlmanacNamespaceEditController extends AlmanacController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
return id(new AlmanacNamespaceEditEngine())
|
||||||
|
->setController($this)
|
||||||
|
->buildResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -55,7 +55,7 @@ final class AlmanacPropertyEditController
|
||||||
} else {
|
} else {
|
||||||
$caught = null;
|
$caught = null;
|
||||||
try {
|
try {
|
||||||
AlmanacNames::validateServiceOrDeviceName($name);
|
AlmanacNames::validateName($name);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$caught = $ex;
|
$caught = $ex;
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ final class AlmanacPropertyEditController
|
||||||
|
|
||||||
// Make sure property key is appropriate.
|
// Make sure property key is appropriate.
|
||||||
// TODO: It would be cleaner to put this safety check in the Editor.
|
// 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
|
// If we're adding a new property, put a placeholder on the object so
|
||||||
// that we can build a CustomField for it.
|
// that we can build a CustomField for it.
|
||||||
|
|
|
@ -136,7 +136,7 @@ final class AlmanacDeviceEditor
|
||||||
$name = $xaction->getNewValue();
|
$name = $xaction->getNewValue();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AlmanacNames::validateServiceOrDeviceName($name);
|
AlmanacNames::validateName($name);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$message = $ex->getMessage();
|
$message = $ex->getMessage();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
162
src/applications/almanac/editor/AlmanacNamespaceEditor.php
Normal file
162
src/applications/almanac/editor/AlmanacNamespaceEditor.php
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class AlmanacNamespaceEditor
|
||||||
|
extends PhabricatorApplicationTransactionEditor {
|
||||||
|
|
||||||
|
public function getEditorApplicationClass() {
|
||||||
|
return 'PhabricatorAlmanacApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditorObjectsDescription() {
|
||||||
|
return pht('Almanac Namespace');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function supportsSearch() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionTypes() {
|
||||||
|
$types = parent::getTransactionTypes();
|
||||||
|
|
||||||
|
$types[] = AlmanacNamespaceTransaction::TYPE_NAME;
|
||||||
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
|
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCustomTransactionOldValue(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
switch ($xaction->getTransactionType()) {
|
||||||
|
case AlmanacNamespaceTransaction::TYPE_NAME:
|
||||||
|
return $object->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getCustomTransactionOldValue($object, $xaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCustomTransactionNewValue(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
switch ($xaction->getTransactionType()) {
|
||||||
|
case AlmanacNamespaceTransaction::TYPE_NAME:
|
||||||
|
return $xaction->getNewValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::getCustomTransactionNewValue($object, $xaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyCustomInternalTransaction(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
switch ($xaction->getTransactionType()) {
|
||||||
|
case AlmanacNamespaceTransaction::TYPE_NAME:
|
||||||
|
$object->setName($xaction->getNewValue());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::applyCustomInternalTransaction($object, $xaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyCustomExternalTransaction(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
|
switch ($xaction->getTransactionType()) {
|
||||||
|
case AlmanacNamespaceTransaction::TYPE_NAME:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::applyCustomExternalTransaction($object, $xaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function validateTransaction(
|
||||||
|
PhabricatorLiskDAO $object,
|
||||||
|
$type,
|
||||||
|
array $xactions) {
|
||||||
|
|
||||||
|
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case AlmanacNamespaceTransaction::TYPE_NAME:
|
||||||
|
$missing = $this->validateIsEmptyTextField(
|
||||||
|
$object->getName(),
|
||||||
|
$xactions);
|
||||||
|
|
||||||
|
if ($missing) {
|
||||||
|
$error = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Required'),
|
||||||
|
pht('Namespace name is required.'),
|
||||||
|
nonempty(last($xactions), null));
|
||||||
|
|
||||||
|
$error->setIsMissingFieldError(true);
|
||||||
|
$errors[] = $error;
|
||||||
|
} else {
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
$name = $xaction->getNewValue();
|
||||||
|
|
||||||
|
$message = null;
|
||||||
|
try {
|
||||||
|
AlmanacNames::validateName($name);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$message = $ex->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($message !== null) {
|
||||||
|
$error = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
$type,
|
||||||
|
pht('Invalid'),
|
||||||
|
$message,
|
||||||
|
$xaction);
|
||||||
|
$errors[] = $error;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -128,7 +128,7 @@ final class AlmanacServiceEditor
|
||||||
$name = $xaction->getNewValue();
|
$name = $xaction->getNewValue();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AlmanacNames::validateServiceOrDeviceName($name);
|
AlmanacNames::validateName($name);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$message = $ex->getMessage();
|
$message = $ex->getMessage();
|
||||||
}
|
}
|
||||||
|
|
44
src/applications/almanac/phid/AlmanacNamespacePHIDType.php
Normal file
44
src/applications/almanac/phid/AlmanacNamespacePHIDType.php
Normal file
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
src/applications/almanac/query/AlmanacNamespaceQuery.php
Normal file
103
src/applications/almanac/query/AlmanacNamespaceQuery.php
Normal file
|
@ -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';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class AlmanacNamespaceSearchEngine
|
||||||
|
extends PhabricatorApplicationSearchEngine {
|
||||||
|
|
||||||
|
public function getResultTypeDescription() {
|
||||||
|
return pht('Almanac Namespaces');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationClassName() {
|
||||||
|
return 'PhabricatorAlmanacApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newQuery() {
|
||||||
|
return new AlmanacNamespaceQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildCustomSearchFields() {
|
||||||
|
return array(
|
||||||
|
id(new PhabricatorSearchTextField())
|
||||||
|
->setLabel(pht('Name Contains'))
|
||||||
|
->setKey('match')
|
||||||
|
->setDescription(pht('Search for namespaces by name substring.')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildQueryFromParameters(array $map) {
|
||||||
|
$query = $this->newQuery();
|
||||||
|
|
||||||
|
if ($map['match'] !== null) {
|
||||||
|
$query->withNameNgrams($map['match']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getURI($path) {
|
||||||
|
return '/almanac/namespace/'.$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getBuiltinQueryNames() {
|
||||||
|
$names = array(
|
||||||
|
'all' => pht('All Namespaces'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildSavedQueryFromBuiltin($query_key) {
|
||||||
|
|
||||||
|
$query = $this->newSavedQuery();
|
||||||
|
$query->setQueryKey($query_key);
|
||||||
|
|
||||||
|
switch ($query_key) {
|
||||||
|
case 'all':
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function renderResultList(
|
||||||
|
array $namespaces,
|
||||||
|
PhabricatorSavedQuery $query,
|
||||||
|
array $handles) {
|
||||||
|
assert_instances_of($namespaces, 'AlmanacNamespace');
|
||||||
|
|
||||||
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$list = new PHUIObjectItemListView();
|
||||||
|
$list->setUser($viewer);
|
||||||
|
foreach ($namespaces as $namespace) {
|
||||||
|
$id = $namespace->getID();
|
||||||
|
|
||||||
|
$item = id(new PHUIObjectItemView())
|
||||||
|
->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 namespaces found.'));
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class AlmanacNamespaceTransactionQuery
|
||||||
|
extends PhabricatorApplicationTransactionQuery {
|
||||||
|
|
||||||
|
public function getTemplateApplicationTransaction() {
|
||||||
|
return new AlmanacNamespaceTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,7 +20,7 @@ final class AlmanacNetworkSearchEngine
|
||||||
id(new PhabricatorSearchTextField())
|
id(new PhabricatorSearchTextField())
|
||||||
->setLabel(pht('Name Contains'))
|
->setLabel(pht('Name Contains'))
|
||||||
->setKey('match')
|
->setKey('match')
|
||||||
->setDescription(pht('Search for devices by name substring.')),
|
->setDescription(pht('Search for networks by name substring.')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ final class AlmanacDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
AlmanacNames::validateServiceOrDeviceName($this->getName());
|
AlmanacNames::validateName($this->getName());
|
||||||
|
|
||||||
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
||||||
|
|
||||||
|
|
197
src/applications/almanac/storage/AlmanacNamespace.php
Normal file
197
src/applications/almanac/storage/AlmanacNamespace.php
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class AlmanacNamespace
|
||||||
|
extends AlmanacDAO
|
||||||
|
implements
|
||||||
|
PhabricatorPolicyInterface,
|
||||||
|
PhabricatorCustomFieldInterface,
|
||||||
|
PhabricatorApplicationTransactionInterface,
|
||||||
|
PhabricatorProjectInterface,
|
||||||
|
AlmanacPropertyInterface,
|
||||||
|
PhabricatorDestructibleInterface,
|
||||||
|
PhabricatorNgramsInterface {
|
||||||
|
|
||||||
|
protected $name;
|
||||||
|
protected $nameIndex;
|
||||||
|
protected $mailKey;
|
||||||
|
protected $viewPolicy;
|
||||||
|
protected $editPolicy;
|
||||||
|
|
||||||
|
private $customFields = self::ATTACHABLE;
|
||||||
|
private $almanacProperties = self::ATTACHABLE;
|
||||||
|
|
||||||
|
public static function initializeNewNamespace() {
|
||||||
|
return id(new self())
|
||||||
|
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||||
|
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||||
|
->attachAlmanacProperties(array());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'name' => 'text128',
|
||||||
|
'nameIndex' => 'bytes12',
|
||||||
|
'mailKey' => 'bytes20',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_nameindex' => array(
|
||||||
|
'columns' => array('nameIndex'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
|
'key_name' => array(
|
||||||
|
'columns' => array('name'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generatePHID() {
|
||||||
|
return PhabricatorPHID::generateNewPHID(
|
||||||
|
AlmanacNamespacePHIDType::TYPECONST);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function save() {
|
||||||
|
AlmanacNames::validateName($this->getName());
|
||||||
|
|
||||||
|
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
||||||
|
|
||||||
|
if (!$this->mailKey) {
|
||||||
|
$this->mailKey = Filesystem::readRandomCharacters(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::save();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getURI() {
|
||||||
|
return '/almanac/namespace/view/'.$this->getName().'/';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( AlmanacPropertyInterface )------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function attachAlmanacProperties(array $properties) {
|
||||||
|
assert_instances_of($properties, 'AlmanacProperty');
|
||||||
|
$this->almanacProperties = mpull($properties, null, 'getFieldName');
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAlmanacProperties() {
|
||||||
|
return $this->assertAttached($this->almanacProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAlmanacProperty($key) {
|
||||||
|
$this->assertAttached($this->almanacProperties);
|
||||||
|
return isset($this->almanacProperties[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAlmanacProperty($key) {
|
||||||
|
return $this->assertAttachedKey($this->almanacProperties, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAlmanacPropertyValue($key, $default = null) {
|
||||||
|
if ($this->hasAlmanacProperty($key)) {
|
||||||
|
return $this->getAlmanacProperty($key)->getFieldValue();
|
||||||
|
} else {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAlmanacPropertyFieldSpecifications() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
switch ($capability) {
|
||||||
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||||
|
return $this->getViewPolicy();
|
||||||
|
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||||
|
return $this->getEditPolicy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAutomaticCapability($capability) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCustomFieldSpecificationForRole($role) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomFieldBaseClass() {
|
||||||
|
return 'AlmanacCustomField';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCustomFields() {
|
||||||
|
return $this->assertAttached($this->customFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
|
||||||
|
$this->customFields = $fields;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getApplicationTransactionEditor() {
|
||||||
|
return new AlmanacNamespaceEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionObject() {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionTemplate() {
|
||||||
|
return new AlmanacNamespaceTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willRenderTimeline(
|
||||||
|
PhabricatorApplicationTransactionView $timeline,
|
||||||
|
AphrontRequest $request) {
|
||||||
|
return $timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function destroyObjectPermanently(
|
||||||
|
PhabricatorDestructionEngine $engine) {
|
||||||
|
$this->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorNgramInterface )------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
public function newNgrams() {
|
||||||
|
return array(
|
||||||
|
id(new AlmanacNamespaceNameNgrams())
|
||||||
|
->setValue($this->getName()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -62,7 +62,7 @@ final class AlmanacService
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
AlmanacNames::validateServiceOrDeviceName($this->getName());
|
AlmanacNames::validateName($this->getName());
|
||||||
|
|
||||||
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
$this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
|
||||||
|
|
||||||
|
|
|
@ -2,54 +2,61 @@
|
||||||
|
|
||||||
final class AlmanacNames extends Phobject {
|
final class AlmanacNames extends Phobject {
|
||||||
|
|
||||||
public static function validateServiceOrDeviceName($name) {
|
public static function validateName($name) {
|
||||||
if (strlen($name) < 3) {
|
if (strlen($name) < 3) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Almanac service and device names must be at least 3 '.
|
'Almanac service, device, property and namespace names must be '.
|
||||||
'characters long.'));
|
'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)) {
|
if (!preg_match('/^[a-z0-9.-]+\z/', $name)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Almanac service and device names may only contain lowercase '.
|
'Almanac service, device, property and namespace names may only '.
|
||||||
'letters, numbers, hyphens, and periods.'));
|
'contain lowercase letters, numbers, hyphens, and periods.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/(^|\\.)\d+(\z|\\.)/', $name)) {
|
if (preg_match('/(^|\\.)\d+(\z|\\.)/', $name)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Almanac service and device names may not have any segments '.
|
'Almanac service, device, property and namespace names may not '.
|
||||||
'containing only digits.'));
|
'have any segments containing only digits.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/\.\./', $name)) {
|
if (preg_match('/\.\./', $name)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Almanac service and device names may not contain multiple '.
|
'Almanac service, device, property and namespace names may not '.
|
||||||
'consecutive periods.'));
|
'contain multiple consecutive periods.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/\\.-|-\\./', $name)) {
|
if (preg_match('/\\.-|-\\./', $name)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Amanac service and device names may not contain hyphens adjacent '.
|
'Almanac service, device, property and namespace names may not '.
|
||||||
'to periods.'));
|
'contain hyphens adjacent to periods.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preg_match('/--/', $name)) {
|
if (preg_match('/--/', $name)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Almanac service and device names may not contain multiple '.
|
'Almanac service, device, property and namespace names may not '.
|
||||||
'consecutive hyphens.'));
|
'contain multiple consecutive hyphens.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^[a-z0-9].*[a-z0-9]\z/', $name)) {
|
if (!preg_match('/^[a-z0-9].*[a-z0-9]\z/', $name)) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Almanac service and device names must begin and end with a letter '.
|
'Almanac service, device, property and namespace names must begin '.
|
||||||
'or number.'));
|
'and end with a letter or number.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,16 @@ final class AlmanacNamesTestCase extends PhabricatorTestCase {
|
||||||
'db.phacility.instance' => true,
|
'db.phacility.instance' => true,
|
||||||
'web002.useast.example.com' => true,
|
'web002.useast.example.com' => true,
|
||||||
'master.example-corp.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) {
|
foreach ($map as $input => $expect) {
|
||||||
$caught = null;
|
$caught = null;
|
||||||
try {
|
try {
|
||||||
AlmanacNames::validateServiceOrDeviceName($input);
|
AlmanacNames::validateName($input);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$caught = $ex;
|
$caught = $ex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,138 @@
|
||||||
@title Almanac User Guide
|
@title Almanac User Guide
|
||||||
@group userguide
|
@group userguide
|
||||||
|
|
||||||
Using Almanac to manage services.
|
Using Almanac to manage devices and services.
|
||||||
|
|
||||||
= Overview =
|
Overview
|
||||||
|
========
|
||||||
|
|
||||||
IMPORTANT: Almanac is a prototype application. See
|
IMPORTANT: Almanac is a prototype application. See
|
||||||
@{article:User Guide: Prototype Applications}.
|
@{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
|
Locking and Unlocking Services
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
@ -17,8 +142,8 @@ services prevents an attacker from modifying the Phabricator cluster definition.
|
||||||
For more details on this scenario, see
|
For more details on this scenario, see
|
||||||
@{article:User Guide: Phabricator Clusters}.
|
@{article:User Guide: Phabricator Clusters}.
|
||||||
|
|
||||||
Beyond hardening cluster definitions, you might also want to lock a service to
|
Beyond hardening cluster definitions, you might also want to lock a critical
|
||||||
prevent accidental edits.
|
service to prevent accidental edits.
|
||||||
|
|
||||||
To lock a service, run:
|
To lock a service, run:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue