Page MenuHomePhabricator

D7638.diff

diff --git a/resources/sql/patches/20131123.drydockblueprintpolicy.sql b/resources/sql/patches/20131123.drydockblueprintpolicy.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/patches/20131123.drydockblueprintpolicy.sql
@@ -0,0 +1,11 @@
+CREATE TABLE {$NAMESPACE}_drydock.drydock_blueprint (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ className VARCHAR(255) NOT NULL COLLATE utf8_bin,
+ viewPolicy VARCHAR(64) NOT NULL,
+ editPolicy VARCHAR(64) NOT NULL,
+ details LONGTEXT CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_phid` (phid)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/resources/sql/patches/20131129.drydockresourceblueprint.sql b/resources/sql/patches/20131129.drydockresourceblueprint.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/patches/20131129.drydockresourceblueprint.sql
@@ -0,0 +1,5 @@
+ALTER TABLE {$NAMESPACE}_drydock.drydock_resource
+ADD COLUMN blueprintPHID VARCHAR(64) NOT NULL COLLATE utf8_bin;
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_resource
+DROP COLUMN blueprintClass;
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
@@ -626,8 +626,14 @@
'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php',
'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php',
'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php',
- 'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php',
+ 'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php',
+ 'DrydockBlueprintCreateController' => 'applications/drydock/controller/DrydockBlueprintCreateController.php',
+ 'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php',
+ 'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php',
+ 'DrydockBlueprintListController' => 'applications/drydock/controller/DrydockBlueprintListController.php',
+ 'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php',
'DrydockBlueprintScopeGuard' => 'applications/drydock/util/DrydockBlueprintScopeGuard.php',
+ 'DrydockBlueprintViewController' => 'applications/drydock/controller/DrydockBlueprintViewController.php',
'DrydockCommandInterface' => 'applications/drydock/interface/command/DrydockCommandInterface.php',
'DrydockConstants' => 'applications/drydock/constants/DrydockConstants.php',
'DrydockController' => 'applications/drydock/controller/DrydockController.php',
@@ -640,7 +646,7 @@
'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php',
'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php',
'DrydockLocalCommandInterface' => 'applications/drydock/interface/command/DrydockLocalCommandInterface.php',
- 'DrydockLocalHostBlueprint' => 'applications/drydock/blueprint/DrydockLocalHostBlueprint.php',
+ 'DrydockLocalHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockLocalHostBlueprintImplementation.php',
'DrydockLog' => 'applications/drydock/storage/DrydockLog.php',
'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php',
'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
@@ -650,16 +656,17 @@
'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php',
'DrydockManagementWaitForLeaseWorkflow' => 'applications/drydock/management/DrydockManagementWaitForLeaseWorkflow.php',
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
- 'DrydockPreallocatedHostBlueprint' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php',
+ 'DrydockPHIDTypeBlueprint' => 'applications/drydock/phid/DrydockPHIDTypeBlueprint.php',
+ 'DrydockPreallocatedHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php',
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php',
'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
'DrydockResourceQuery' => 'applications/drydock/query/DrydockResourceQuery.php',
'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php',
'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php',
'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php',
- 'DrydockWorkingCopyBlueprint' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprint.php',
+ 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php',
'FeedPublisherHTTPWorker' => 'applications/feed/worker/FeedPublisherHTTPWorker.php',
'FeedPublisherWorker' => 'applications/feed/worker/FeedPublisherWorker.php',
'FeedPushWorker' => 'applications/feed/worker/FeedPushWorker.php',
@@ -2951,6 +2958,16 @@
'DoorkeeperTagsController' => 'PhabricatorController',
'DrydockAllocatorWorker' => 'PhabricatorWorker',
'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface',
+ 'DrydockBlueprint' =>
+ array(
+ 0 => 'DrydockDAO',
+ 1 => 'PhabricatorPolicyInterface',
+ ),
+ 'DrydockBlueprintCreateController' => 'DrydockController',
+ 'DrydockBlueprintEditController' => 'DrydockController',
+ 'DrydockBlueprintListController' => 'DrydockController',
+ 'DrydockBlueprintQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'DrydockBlueprintViewController' => 'DrydockController',
'DrydockCommandInterface' => 'DrydockInterface',
'DrydockController' => 'PhabricatorController',
'DrydockDAO' => 'PhabricatorLiskDAO',
@@ -2961,7 +2978,7 @@
'DrydockLeaseStatus' => 'DrydockConstants',
'DrydockLeaseViewController' => 'DrydockController',
'DrydockLocalCommandInterface' => 'DrydockCommandInterface',
- 'DrydockLocalHostBlueprint' => 'DrydockBlueprint',
+ 'DrydockLocalHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockLog' => 'DrydockDAO',
'DrydockLogController' => 'DrydockController',
'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery',
@@ -2971,7 +2988,8 @@
'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementWaitForLeaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementWorkflow' => 'PhutilArgumentWorkflow',
- 'DrydockPreallocatedHostBlueprint' => 'DrydockBlueprint',
+ 'DrydockPHIDTypeBlueprint' => 'PhabricatorPHIDType',
+ 'DrydockPreallocatedHostBlueprintImplementation' => 'DrydockBlueprintImplementation',
'DrydockResource' =>
array(
0 => 'DrydockDAO',
@@ -2984,7 +3002,7 @@
'DrydockResourceViewController' => 'DrydockController',
'DrydockSSHCommandInterface' => 'DrydockCommandInterface',
'DrydockWebrootInterface' => 'DrydockInterface',
- 'DrydockWorkingCopyBlueprint' => 'DrydockBlueprint',
+ 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation',
'FeedPublisherHTTPWorker' => 'FeedPushWorker',
'FeedPublisherWorker' => 'FeedPushWorker',
'FeedPushWorker' => 'PhabricatorWorker',
diff --git a/src/applications/drydock/application/PhabricatorApplicationDrydock.php b/src/applications/drydock/application/PhabricatorApplicationDrydock.php
--- a/src/applications/drydock/application/PhabricatorApplicationDrydock.php
+++ b/src/applications/drydock/application/PhabricatorApplicationDrydock.php
@@ -34,6 +34,12 @@
return array(
'/drydock/' => array(
'' => 'DrydockResourceListController',
+ 'blueprint/' => array(
+ '' => 'DrydockBlueprintListController',
+ '(?P<id>[1-9]\d*)/' => 'DrydockBlueprintViewController',
+ 'create/' => 'DrydockBlueprintCreateController',
+ 'edit/(?P<id>[1-9]\d*)/' => 'DrydockBlueprintEditController',
+ ),
'resource/' => array(
'' => 'DrydockResourceListController',
'(?P<id>[1-9]\d*)/' => 'DrydockResourceViewController',
diff --git a/src/applications/drydock/blueprint/DrydockBlueprint.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
rename from src/applications/drydock/blueprint/DrydockBlueprint.php
rename to src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockBlueprint.php
+++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
@@ -5,10 +5,11 @@
* @task resource Resource Allocation
* @task log Logging
*/
-abstract class DrydockBlueprint {
+abstract class DrydockBlueprintImplementation {
private $activeResource;
private $activeLease;
+ private $instance;
abstract public function getType();
abstract public function getInterface(
@@ -18,6 +19,8 @@
abstract public function isEnabled();
+ abstract public function getDescription();
+
public function getBlueprintClass() {
return get_class($this);
}
@@ -37,6 +40,20 @@
return $lease;
}
+ protected function getInstance() {
+ if (!$this->instance) {
+ throw new Exception(
+ "Attach the blueprint instance to the implementation.");
+ }
+
+ return $this->instance;
+ }
+
+ public function attachInstance(DrydockBlueprint $instance) {
+ $this->instance = $instance;
+ return $this;
+ }
+
/* -( Lease Acquisition )-------------------------------------------------- */
@@ -343,13 +360,13 @@
}
- public static function getAllBlueprints() {
+ public static function getAllBlueprintImplementations() {
static $list = null;
if ($list === null) {
$blueprints = id(new PhutilSymbolLoader())
->setType('class')
- ->setAncestorClass('DrydockBlueprint')
+ ->setAncestorClass('DrydockBlueprintImplementation')
->setConcreteOnly(true)
->selectAndLoadSymbols();
$list = ipull($blueprints, 'name', 'name');
@@ -361,16 +378,17 @@
return $list;
}
- public static function getAllBlueprintsForResource($type) {
+ public static function getAllBlueprintImplementationsForResource($type) {
static $groups = null;
if ($groups === null) {
- $groups = mgroup(self::getAllBlueprints(), 'getType');
+ $groups = mgroup(self::getAllBlueprintImplementations(), 'getType');
}
return idx($groups, $type, array());
}
protected function newResourceTemplate($name) {
$resource = new DrydockResource();
+ $resource->setBlueprintPHID($this->getInstance()->getPHID());
$resource->setBlueprintClass($this->getBlueprintClass());
$resource->setType($this->getType());
$resource->setStatus(DrydockResourceStatus::STATUS_PENDING);
diff --git a/src/applications/drydock/blueprint/DrydockLocalHostBlueprint.php b/src/applications/drydock/blueprint/DrydockLocalHostBlueprintImplementation.php
rename from src/applications/drydock/blueprint/DrydockLocalHostBlueprint.php
rename to src/applications/drydock/blueprint/DrydockLocalHostBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockLocalHostBlueprint.php
+++ b/src/applications/drydock/blueprint/DrydockLocalHostBlueprintImplementation.php
@@ -1,11 +1,16 @@
<?php
-final class DrydockLocalHostBlueprint extends DrydockBlueprint {
+final class DrydockLocalHostBlueprintImplementation
+ extends DrydockBlueprintImplementation {
public function isEnabled() {
return false;
}
+ public function getDescription() {
+ return pht('Allocates storage on the local host.');
+ }
+
public function canAllocateMoreResources(array $pool) {
assert_instances_of($pool, 'DrydockResource');
diff --git a/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php b/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
rename from src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php
rename to src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php
+++ b/src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
@@ -1,11 +1,16 @@
<?php
-final class DrydockPreallocatedHostBlueprint extends DrydockBlueprint {
+final class DrydockPreallocatedHostBlueprintImplementation
+ extends DrydockBlueprintImplementation {
public function isEnabled() {
return true;
}
+ public function getDescription() {
+ return pht('Leases out preallocated, remote hosts.');
+ }
+
public function canAllocateMoreResources(array $pool) {
return false;
}
diff --git a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprint.php b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
rename from src/applications/drydock/blueprint/DrydockWorkingCopyBlueprint.php
rename to src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
--- a/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprint.php
+++ b/src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
@@ -1,11 +1,16 @@
<?php
-final class DrydockWorkingCopyBlueprint extends DrydockBlueprint {
+final class DrydockWorkingCopyBlueprintImplementation
+ extends DrydockBlueprintImplementation {
public function isEnabled() {
return true;
}
+ public function getDescription() {
+ return pht('Allocates out working copies of repositories.');
+ }
+
protected function canAllocateLease(
DrydockResource $resource,
DrydockLease $lease) {
diff --git a/src/applications/drydock/controller/DrydockBlueprintCreateController.php b/src/applications/drydock/controller/DrydockBlueprintCreateController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockBlueprintCreateController.php
@@ -0,0 +1,68 @@
+<?php
+
+final class DrydockBlueprintCreateController
+ extends DrydockController {
+
+ public function willProcessRequest(array $data) {
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $implementations =
+ DrydockBlueprintImplementation::getAllBlueprintImplementations();
+
+ if ($request->isFormPost()) {
+ $class = $request->getStr('blueprint-type');
+ if (!isset($implementations[$class])) {
+ return $this->createDialog($implementations);
+ }
+
+ $blueprint = new DrydockBlueprint();
+ $blueprint->setClassName($class);
+ $blueprint->setDetails(array());
+ $blueprint->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN);
+ $blueprint->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN);
+ $blueprint->save();
+
+ $edit_uri = $this->getApplicationURI(
+ "blueprint/edit/".$blueprint->getID()."/");
+
+ return id(new AphrontRedirectResponse())->setURI($edit_uri);
+ }
+
+ return $this->createDialog($implementations);
+ }
+
+ function createDialog(array $implementations) {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $control = id(new AphrontFormRadioButtonControl())
+ ->setName('blueprint-type');
+
+ foreach ($implementations as $implementation_name => $implementation) {
+ $control
+ ->addButton(
+ $implementation_name,
+ $implementation->getBlueprintClass(),
+ $implementation->getDescription());
+ }
+
+ $dialog = new AphrontDialogView();
+ $dialog->setTitle(pht('Create New Blueprint'))
+ ->setUser($viewer)
+ ->addSubmitButton(pht('Create Blueprint'))
+ ->addCancelButton($this->getApplicationURI('blueprint/'));
+ $dialog->appendChild(
+ phutil_tag(
+ 'p',
+ array(),
+ pht(
+ 'Select what type of blueprint you want to create: ')));
+ $dialog->appendChild($control);
+ return id(new AphrontDialogResponse())->setDialog($dialog);
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockBlueprintEditController.php b/src/applications/drydock/controller/DrydockBlueprintEditController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockBlueprintEditController.php
@@ -0,0 +1,122 @@
+<?php
+
+final class DrydockBlueprintEditController extends DrydockController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = idx($data, 'id');
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ if ($this->id) {
+ $blueprint = id(new DrydockBlueprintQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($this->id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$blueprint) {
+ return new Aphront404Response();
+ }
+ } else {
+ $blueprint = new DrydockBlueprint();
+ }
+
+ if ($request->isFormPost()) {
+ $v_view_policy = $request->getStr('viewPolicy');
+ $v_edit_policy = $request->getStr('editPolicy');
+
+ // TODO: Should we use transactions here?
+ $blueprint->setViewPolicy($v_view_policy);
+ $blueprint->setEditPolicy($v_edit_policy);
+
+ $blueprint->save();
+
+ return id(new AphrontRedirectResponse())
+ ->setURI('/drydock/blueprint/');
+ }
+
+ $policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($blueprint)
+ ->execute();
+
+ if ($request->isAjax()) {
+ $form = id(new PHUIFormLayoutView())
+ ->setUser($viewer);
+ } else {
+ $form = id(new AphrontFormView())
+ ->setUser($viewer);
+ }
+
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName('className')
+ ->setLabel(pht('Implementation'))
+ ->setValue($blueprint->getClassName())
+ ->setDisabled(true))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('viewPolicy')
+ ->setPolicyObject($blueprint)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('editPolicy')
+ ->setPolicyObject($blueprint)
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
+ ->setPolicies($policies));
+
+ $crumbs = $this->buildApplicationCrumbs();
+
+ $title = pht('Edit Blueprint');
+ $header = pht('Edit Blueprint %d', $blueprint->getID());
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Blueprint %d', $blueprint->getID())));
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Edit')));
+
+ if ($request->isAjax()) {
+ $dialog = id(new AphrontDialogView())
+ ->setUser($viewer)
+ ->setWidth(AphrontDialogView::WIDTH_FORM)
+ ->setTitle($title)
+ ->appendChild($form)
+ ->addSubmitButton(pht('Edit Blueprint'))
+ ->addCancelButton($this->getApplicationURI());
+
+ return id(new AphrontDialogResponse())->setDialog($dialog);
+ }
+
+ $form->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Save'))
+ ->addCancelButton($this->getApplicationURI()));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeaderText($header)
+ ->setForm($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box,
+ ),
+ array(
+ 'title' => $title,
+ 'device' => true,
+ ));
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockBlueprintListController.php b/src/applications/drydock/controller/DrydockBlueprintListController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockBlueprintListController.php
@@ -0,0 +1,77 @@
+<?php
+
+final class DrydockBlueprintListController extends DrydockController {
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $title = pht('Blueprints');
+
+ $blueprint_header = id(new PHUIHeaderView())
+ ->setHeader($title);
+
+ $blueprints = id(new DrydockBlueprintQuery())
+ ->setViewer($user)
+ ->execute();
+
+ $blueprint_list = $this->buildBlueprintListView($blueprints);
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName($title)
+ ->setHref($request->getRequestURI()));
+
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('New Blueprint'))
+ ->setHref($this->getApplicationURI('blueprint/create/'))
+ ->setIcon('create'));
+
+ $nav = $this->buildSideNav('blueprint');
+ $nav->setCrumbs($crumbs);
+ $nav->appendChild(
+ array(
+ $blueprint_header,
+ $blueprint_list
+ ));
+
+ return $this->buildApplicationPage(
+ $nav,
+ array(
+ 'title' => $title,
+ 'device' => true,
+ ));
+
+ }
+
+ protected function buildBlueprintListView(array $blueprints) {
+ assert_instances_of($blueprints, 'DrydockBlueprint');
+
+ $user = $this->getRequest()->getUser();
+ $view = new PHUIObjectItemListView();
+
+ foreach ($blueprints as $blueprint) {
+ $item = id(new PHUIObjectItemView())
+ ->setHeader($blueprint->getClassName())
+ ->setHref($this->getApplicationURI('/blueprint/'.$blueprint->getID()))
+ ->setObjectName(pht('Blueprint %d', $blueprint->getID()));
+
+ if ($blueprint->getImplementation()->isEnabled()) {
+ $item->addAttribute(pht('Enabled'));
+ $item->setBarColor('green');
+ } else {
+ $item->addAttribute(pht('Disabled'));
+ $item->setBarColor('red');
+ }
+
+ $item->addAttribute($blueprint->getImplementation()->getDescription());
+
+ $view->addItem($item);
+ }
+
+ return $view;
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php
@@ -0,0 +1,100 @@
+<?php
+
+final class DrydockBlueprintViewController extends DrydockController {
+
+ private $id;
+
+ public function willProcessRequest(array $data) {
+ $this->id = $data['id'];
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $user = $request->getUser();
+
+ $blueprint = id(new DrydockBlueprint())->load($this->id);
+ if (!$blueprint) {
+ return new Aphront404Response();
+ }
+
+ $title = 'Blueprint '.$blueprint->getID().' '.$blueprint->getClassName();
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader($title);
+
+ $actions = $this->buildActionListView($blueprint);
+ $properties = $this->buildPropertyListView($blueprint, $actions);
+
+ $blueprint_uri = 'blueprint/'.$blueprint->getID().'/';
+ $blueprint_uri = $this->getApplicationURI($blueprint_uri);
+
+ $resources = id(new DrydockResourceQuery())
+ ->withBlueprintPHIDs(array($blueprint->getPHID()))
+ ->setViewer($user)
+ ->execute();
+
+ $resource_list = $this->buildResourceListView($resources);
+ $resource_list->setNoDataString(pht('This blueprint has no resources.'));
+
+ $pager = new AphrontPagerView();
+ $pager->setURI(new PhutilURI($blueprint_uri), 'offset');
+ $pager->setOffset($request->getInt('offset'));
+
+ $crumbs = $this->buildApplicationCrumbs();
+ $crumbs->setActionList($actions);
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Blueprint %d', $blueprint->getID())));
+
+ $object_box = id(new PHUIObjectBoxView())
+ ->setHeader($header)
+ ->addPropertyList($properties);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $object_box,
+ $resource_list
+ ),
+ array(
+ 'device' => true,
+ 'title' => $title,
+ ));
+
+ }
+
+ private function buildActionListView(DrydockBlueprint $blueprint) {
+ $view = id(new PhabricatorActionListView())
+ ->setUser($this->getRequest()->getUser())
+ ->setObjectURI($this->getRequest()->getRequestURI())
+ ->setObject($blueprint);
+
+ $uri = '/blueprint/edit/'.$blueprint->getID().'/';
+ $uri = $this->getApplicationURI($uri);
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setHref($uri)
+ ->setName(pht('Edit Blueprint Policies'))
+ ->setIcon('edit')
+ ->setWorkflow(true)
+ ->setDisabled(false));
+
+ return $view;
+ }
+
+ private function buildPropertyListView(
+ DrydockBlueprint $blueprint,
+ PhabricatorActionListView $actions) {
+
+ $view = new PHUIPropertyListView();
+ $view->setActionList($actions);
+
+ $view->addProperty(
+ pht('Implementation'),
+ $blueprint->getClassName());
+
+ return $view;
+ }
+
+}
diff --git a/src/applications/drydock/controller/DrydockController.php b/src/applications/drydock/controller/DrydockController.php
--- a/src/applications/drydock/controller/DrydockController.php
+++ b/src/applications/drydock/controller/DrydockController.php
@@ -5,9 +5,10 @@
final protected function buildSideNav($selected) {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/drydock/'));
- $nav->addFilter('resource', 'Resources');
- $nav->addFilter('lease', 'Leases');
- $nav->addFilter('log', 'Logs');
+ $nav->addFilter('blueprint', 'Blueprints');
+ $nav->addFilter('resource', 'Resources');
+ $nav->addFilter('lease', 'Leases');
+ $nav->addFilter('log', 'Logs');
$nav->selectFilter($selected, 'resource');
diff --git a/src/applications/drydock/controller/DrydockResourceViewController.php b/src/applications/drydock/controller/DrydockResourceViewController.php
--- a/src/applications/drydock/controller/DrydockResourceViewController.php
+++ b/src/applications/drydock/controller/DrydockResourceViewController.php
@@ -33,9 +33,6 @@
->needResources(true)
->execute();
- $lease_header = id(new PHUIHeaderView())
- ->setHeader(pht('Leases'));
-
$lease_list = $this->buildLeaseListView($leases);
$lease_list->setNoDataString(pht('This resource has no leases.'));
@@ -64,7 +61,6 @@
array(
$crumbs,
$object_box,
- $lease_header,
$lease_list,
$log_table,
),
@@ -114,6 +110,11 @@
pht('Resource Type'),
$resource->getType());
+ // TODO: Load handle.
+ $view->addProperty(
+ pht('Blueprint'),
+ $resource->getBlueprintPHID());
+
$attributes = $resource->getAttributes();
if ($attributes) {
$view->addSectionHeader(pht('Attributes'));
diff --git a/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php b/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
--- a/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
+++ b/src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
@@ -16,8 +16,8 @@
),
array(
'name' => 'blueprint',
- 'param' => 'blueprint_type',
- 'help' => 'Blueprint type.',
+ 'param' => 'blueprint_id',
+ 'help' => 'Blueprint ID.',
),
array(
'name' => 'attributes',
@@ -36,10 +36,10 @@
"Specify a resource name with `--name`.");
}
- $blueprint_type = $args->getArg('blueprint');
- if (!$blueprint_type) {
+ $blueprint_id = $args->getArg('blueprint');
+ if (!$blueprint_id) {
throw new PhutilArgumentUsageException(
- "Specify a blueprint type with `--blueprint`.");
+ "Specify a blueprint ID with `--blueprint`.");
}
$attributes = $args->getArg('attributes');
@@ -49,9 +49,15 @@
$attributes = $options->parse($attributes);
}
+ $blueprint = id(new DrydockBlueprint())->load((int)$blueprint_id);
+ if (!$blueprint) {
+ throw new PhutilArgumentUsageException(
+ "Specified blueprint does not exist.");
+ }
+
$resource = new DrydockResource();
- $resource->setBlueprintClass($blueprint_type);
- $resource->setType(id(new $blueprint_type())->getType());
+ $resource->setBlueprintPHID($blueprint->getPHID());
+ $resource->setType($blueprint->getImplementation()->getType());
$resource->setName($resource_name);
$resource->setStatus(DrydockResourceStatus::STATUS_OPEN);
if ($attributes) {
diff --git a/src/applications/drydock/phid/DrydockPHIDTypeBlueprint.php b/src/applications/drydock/phid/DrydockPHIDTypeBlueprint.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/phid/DrydockPHIDTypeBlueprint.php
@@ -0,0 +1,34 @@
+<?php
+
+final class DrydockPHIDTypeBlueprint extends PhabricatorPHIDType {
+
+ const TYPECONST = 'DRYB';
+
+ public function getTypeConstant() {
+ return self::TYPECONST;
+ }
+
+ public function getTypeName() {
+ return pht('Blueprint');
+ }
+
+ public function newObject() {
+ return new DrydockBlueprint();
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new DrydockBlueprintQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ }
+
+}
diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockBlueprintQuery.php
copy from src/applications/drydock/query/DrydockResourceQuery.php
copy to src/applications/drydock/query/DrydockBlueprintQuery.php
--- a/src/applications/drydock/query/DrydockResourceQuery.php
+++ b/src/applications/drydock/query/DrydockBlueprintQuery.php
@@ -1,42 +1,46 @@
<?php
-final class DrydockResourceQuery
+final class DrydockBlueprintQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
- private $statuses;
- private $types;
+ private $phids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
- public function withTypes(array $types) {
- $this->types = $types;
- return $this;
- }
-
- public function withStatuses(array $statuses) {
- $this->statuses = $statuses;
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
return $this;
}
public function loadPage() {
- $table = new DrydockResource();
+ $table = new DrydockBlueprint();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
- 'SELECT resource.* FROM %T resource %Q %Q %Q',
+ 'SELECT blueprint.* FROM %T blueprint %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
- $resources = $table->loadAllFromArray($data);
+ $blueprints = $table->loadAllFromArray($data);
+
+ $implementations =
+ DrydockBlueprintImplementation::getAllBlueprintImplementations();
+
+ foreach ($blueprints as $blueprint) {
+ if (array_key_exists($implementations, $blueprint->getClassName())) {
+ $blueprint->attachImplementation(
+ $implementations[$blueprint->getClassName()]);
+ }
+ }
- return $resources;
+ return $blueprints;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
@@ -49,22 +53,13 @@
$this->ids);
}
- if ($this->types) {
+ if ($this->phids) {
$where[] = qsprintf(
$conn_r,
- 'type IN (%Ls)',
- $this->types);
+ 'phid IN (%Ld)',
+ $this->phids);
}
- if ($this->statuses) {
- $where[] = qsprintf(
- $conn_r,
- 'status IN (%Ls)',
- $this->statuses);
- }
-
- $where[] = $this->buildPagingClause($conn_r);
-
return $this->formatWhereClause($where);
}
diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php
--- a/src/applications/drydock/query/DrydockLeaseQuery.php
+++ b/src/applications/drydock/query/DrydockLeaseQuery.php
@@ -40,12 +40,16 @@
'id IN (%Ld)',
mpull($leases, 'getResourceID'));
- foreach ($leases as $lease) {
+ foreach ($leases as $key => $lease) {
if ($lease->getResourceID()) {
$resource = idx($resources, $lease->getResourceID());
if ($resource) {
$lease->attachResource($resource);
+ } else {
+ unset($leases[$key]);
}
+ } else {
+ unset($leases[$key]);
}
}
}
diff --git a/src/applications/drydock/query/DrydockResourceQuery.php b/src/applications/drydock/query/DrydockResourceQuery.php
--- a/src/applications/drydock/query/DrydockResourceQuery.php
+++ b/src/applications/drydock/query/DrydockResourceQuery.php
@@ -6,6 +6,7 @@
private $ids;
private $statuses;
private $types;
+ private $blueprintPHIDs;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -22,6 +23,11 @@
return $this;
}
+ public function withBlueprintPHIDs(array $blueprint_phids) {
+ $this->blueprintPHIDs = $blueprint_phids;
+ return $this;
+ }
+
public function loadPage() {
$table = new DrydockResource();
$conn_r = $table->establishConnection('r');
@@ -63,6 +69,13 @@
$this->statuses);
}
+ if ($this->blueprintPHIDs) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'blueprintPHID IN (%Ls)',
+ $this->blueprintPHIDs);
+ }
+
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
diff --git a/src/applications/drydock/storage/DrydockBlueprint.php b/src/applications/drydock/storage/DrydockBlueprint.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/storage/DrydockBlueprint.php
@@ -0,0 +1,72 @@
+<?php
+
+final class DrydockBlueprint extends DrydockDAO
+ implements PhabricatorPolicyInterface {
+
+ protected $phid;
+ protected $className;
+ protected $viewPolicy;
+ protected $editPolicy;
+ protected $details;
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_SERIALIZATION => array(
+ 'details' => self::SERIALIZATION_JSON,
+ )
+ ) + parent::getConfiguration();
+ }
+
+ public function generatePHID() {
+ return PhabricatorPHID::generateNewPHID(
+ DrydockPHIDTypeBlueprint::TYPECONST);
+ }
+
+ public function getImplementation() {
+ $class = $this->className;
+ $implementations =
+ DrydockBlueprintImplementation::getAllBlueprintImplementations();
+ if (!isset($implementations[$class])) {
+ throw new Exception(
+ "Invalid class name for blueprint (got '".$class."')");
+ }
+ return id(new $class())->attachInstance($this);
+ }
+
+/* -( 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) {
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ case PhabricatorPolicyCapability::CAN_EDIT:
+ return $viewer->getIsAdmin();
+ }
+ }
+
+ public function describeAutomaticCapability($capability) {
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ return pht('Administrators can always view blueprints.');
+ case PhabricatorPolicyCapability::CAN_EDIT:
+ return pht('Administrators can always edit blueprints.');
+ }
+ }
+}
diff --git a/src/applications/drydock/storage/DrydockResource.php b/src/applications/drydock/storage/DrydockResource.php
--- a/src/applications/drydock/storage/DrydockResource.php
+++ b/src/applications/drydock/storage/DrydockResource.php
@@ -5,7 +5,7 @@
protected $id;
protected $phid;
- protected $blueprintClass;
+ protected $blueprintPHID;
protected $status;
protected $type;
@@ -50,7 +50,9 @@
public function getBlueprint() {
if (empty($this->blueprint)) {
- $this->blueprint = newv($this->blueprintClass, array());
+ $blueprint = id(new DrydockBlueprint())
+ ->loadOneWhere('phid = %s', $this->blueprintPHID);
+ $this->blueprint = $blueprint->getImplementation();
}
return $this->blueprint;
}
@@ -76,7 +78,7 @@
$lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
break;
}
- DrydockBlueprint::writeLog($this, $lease, $message);
+ DrydockBlueprintImplementation::writeLog($this, $lease, $message);
$lease->save();
}
diff --git a/src/applications/drydock/util/DrydockBlueprintScopeGuard.php b/src/applications/drydock/util/DrydockBlueprintScopeGuard.php
--- a/src/applications/drydock/util/DrydockBlueprintScopeGuard.php
+++ b/src/applications/drydock/util/DrydockBlueprintScopeGuard.php
@@ -2,7 +2,7 @@
final class DrydockBlueprintScopeGuard {
- public function __construct(DrydockBlueprint $blueprint) {
+ public function __construct(DrydockBlueprintImplementation $blueprint) {
$this->blueprint = $blueprint;
}
diff --git a/src/applications/drydock/worker/DrydockAllocatorWorker.php b/src/applications/drydock/worker/DrydockAllocatorWorker.php
--- a/src/applications/drydock/worker/DrydockAllocatorWorker.php
+++ b/src/applications/drydock/worker/DrydockAllocatorWorker.php
@@ -24,7 +24,7 @@
}
private function logToDrydock($message) {
- DrydockBlueprint::writeLog(
+ DrydockBlueprintImplementation::writeLog(
null,
$this->loadLease(),
$message);
@@ -52,9 +52,20 @@
}
}
+ private function loadAllBlueprints() {
+ $instances = id(new DrydockBlueprint())->loadAll();
+ $blueprints = array();
+ foreach ($instances as $instance) {
+ $blueprints[$instance->getPHID()] = $instance;
+ }
+ return $blueprints;
+ }
+
private function allocateLease(DrydockLease $lease) {
$type = $lease->getResourceType();
+ $blueprints = $this->loadAllBlueprints();
+
$pool = id(new DrydockResource())->loadAllWhere(
'type = %s AND status = %s',
$lease->getResourceType(),
@@ -65,14 +76,15 @@
$candidates = array();
foreach ($pool as $key => $candidate) {
- try {
- $blueprint = $candidate->getBlueprint();
- } catch (Exception $ex) {
+ if (!isset($blueprints[$candidate->getBlueprintPHID()])) {
unset($pool[$key]);
continue;
}
- if ($blueprint->filterResource($candidate, $lease)) {
+ $blueprint = $blueprints[$candidate->getBlueprintPHID()];
+ $implementation = $blueprint->getImplementation();
+
+ if ($implementation->filterResource($candidate, $lease)) {
$candidates[] = $candidate;
}
}
@@ -83,16 +95,18 @@
if ($candidates) {
shuffle($candidates);
foreach ($candidates as $candidate_resource) {
- $blueprint = $candidate_resource->getBlueprint();
+ $blueprint = $blueprints[$candidate_resource->getBlueprintPHID()]
+ ->getImplementation();
if ($blueprint->allocateLease($candidate_resource, $lease)) {
$resource = $candidate_resource;
break;
}
}
}
if (!$resource) {
- $blueprints = DrydockBlueprint::getAllBlueprintsForResource($type);
+ $blueprints = DrydockBlueprintImplementation
+ ::getAllBlueprintImplementationsForResource($type);
$this->logToDrydock(
pht('Found %d Blueprints', count($blueprints)));
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1788,6 +1788,14 @@
'type' => 'sql',
'name' => $this->getPatchPath('20131122.repomirror.sql'),
),
+ '20131123.drydockblueprintpolicy.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131123.drydockblueprintpolicy.sql'),
+ ),
+ '20131129.drydockresourceblueprint.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131129.drydockresourceblueprint.sql'),
+ ),
);
}
}
diff --git a/src/view/form/PHUIFormLayoutView.php b/src/view/form/PHUIFormLayoutView.php
--- a/src/view/form/PHUIFormLayoutView.php
+++ b/src/view/form/PHUIFormLayoutView.php
@@ -14,6 +14,29 @@
return $this;
}
+ public function appendInstructions($text) {
+ return $this->appendChild(
+ phutil_tag(
+ 'div',
+ array(
+ 'class' => 'aphront-form-instructions',
+ ),
+ $text));
+ }
+
+ public function appendRemarkupInstructions($remarkup) {
+ if ($this->getUser() === null) {
+ throw new Exception(
+ "Call `setUser` before appending Remarkup to PHUIFormLayoutView.");
+ }
+
+ return $this->appendInstructions(
+ PhabricatorMarkupEngine::renderOneObject(
+ id(new PhabricatorMarkupOneOff())->setContent($remarkup),
+ 'default',
+ $this->getUser()));
+ }
+
public function render() {
$classes = array('phui-form-view');

File Metadata

Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/bw/u6/ynqhnl3zbydbazae
Default Alt Text
D7638.diff (41 KB)

Event Timeline