@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Restructure Drydock so that blueprints are instances in the DB

Summary:
//(this diff used to be about applying policies to blueprints)//

This restructures Drydock so that blueprints are instances in the DB, with an associated implementation class. Thus resources now have a `blueprintPHID` instead of `blueprintClass` and DrydockBlueprint becomes a DAO. The old DrydockBlueprint is renamed to DrydockBlueprintImplementation, and the DrydockBlueprint DAO has a `blueprintClass` column on it.

This now just implements CAN_VIEW and CAN_EDIT policies for blueprints, although they are probably not enforced in all of the places they could be.

Test Plan: Used the `create-resource` and `lease` commands. Closed resources and leases in the UI. Clicked around the new and old lists to make sure everything is still working.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T4111, T2015

Differential Revision: https://secure.phabricator.com/D7638

+729 -41
+11
resources/sql/patches/20131123.drydockblueprintpolicy.sql
··· 1 + CREATE TABLE {$NAMESPACE}_drydock.drydock_blueprint ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + className VARCHAR(255) NOT NULL COLLATE utf8_bin, 5 + viewPolicy VARCHAR(64) NOT NULL, 6 + editPolicy VARCHAR(64) NOT NULL, 7 + details LONGTEXT CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 8 + dateCreated INT UNSIGNED NOT NULL, 9 + dateModified INT UNSIGNED NOT NULL, 10 + UNIQUE KEY `key_phid` (phid) 11 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+5
resources/sql/patches/20131129.drydockresourceblueprint.sql
··· 1 + ALTER TABLE {$NAMESPACE}_drydock.drydock_resource 2 + ADD COLUMN blueprintPHID VARCHAR(64) NOT NULL COLLATE utf8_bin; 3 + 4 + ALTER TABLE {$NAMESPACE}_drydock.drydock_resource 5 + DROP COLUMN blueprintClass;
+25 -7
src/__phutil_library_map__.php
··· 626 626 'DoorkeeperTagsController' => 'applications/doorkeeper/controller/DoorkeeperTagsController.php', 627 627 'DrydockAllocatorWorker' => 'applications/drydock/worker/DrydockAllocatorWorker.php', 628 628 'DrydockApacheWebrootInterface' => 'applications/drydock/interface/webroot/DrydockApacheWebrootInterface.php', 629 - 'DrydockBlueprint' => 'applications/drydock/blueprint/DrydockBlueprint.php', 629 + 'DrydockBlueprint' => 'applications/drydock/storage/DrydockBlueprint.php', 630 + 'DrydockBlueprintCreateController' => 'applications/drydock/controller/DrydockBlueprintCreateController.php', 631 + 'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php', 632 + 'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php', 633 + 'DrydockBlueprintListController' => 'applications/drydock/controller/DrydockBlueprintListController.php', 634 + 'DrydockBlueprintQuery' => 'applications/drydock/query/DrydockBlueprintQuery.php', 630 635 'DrydockBlueprintScopeGuard' => 'applications/drydock/util/DrydockBlueprintScopeGuard.php', 636 + 'DrydockBlueprintViewController' => 'applications/drydock/controller/DrydockBlueprintViewController.php', 631 637 'DrydockCommandInterface' => 'applications/drydock/interface/command/DrydockCommandInterface.php', 632 638 'DrydockConstants' => 'applications/drydock/constants/DrydockConstants.php', 633 639 'DrydockController' => 'applications/drydock/controller/DrydockController.php', ··· 640 646 'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php', 641 647 'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php', 642 648 'DrydockLocalCommandInterface' => 'applications/drydock/interface/command/DrydockLocalCommandInterface.php', 643 - 'DrydockLocalHostBlueprint' => 'applications/drydock/blueprint/DrydockLocalHostBlueprint.php', 649 + 'DrydockLocalHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockLocalHostBlueprintImplementation.php', 644 650 'DrydockLog' => 'applications/drydock/storage/DrydockLog.php', 645 651 'DrydockLogController' => 'applications/drydock/controller/DrydockLogController.php', 646 652 'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php', ··· 650 656 'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php', 651 657 'DrydockManagementWaitForLeaseWorkflow' => 'applications/drydock/management/DrydockManagementWaitForLeaseWorkflow.php', 652 658 'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php', 653 - 'DrydockPreallocatedHostBlueprint' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php', 659 + 'DrydockPHIDTypeBlueprint' => 'applications/drydock/phid/DrydockPHIDTypeBlueprint.php', 660 + 'DrydockPreallocatedHostBlueprintImplementation' => 'applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php', 654 661 'DrydockResource' => 'applications/drydock/storage/DrydockResource.php', 655 662 'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php', 656 663 'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php', ··· 659 666 'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php', 660 667 'DrydockSSHCommandInterface' => 'applications/drydock/interface/command/DrydockSSHCommandInterface.php', 661 668 'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php', 662 - 'DrydockWorkingCopyBlueprint' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprint.php', 669 + 'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php', 663 670 'FeedPublisherHTTPWorker' => 'applications/feed/worker/FeedPublisherHTTPWorker.php', 664 671 'FeedPublisherWorker' => 'applications/feed/worker/FeedPublisherWorker.php', 665 672 'FeedPushWorker' => 'applications/feed/worker/FeedPushWorker.php', ··· 2951 2958 'DoorkeeperTagsController' => 'PhabricatorController', 2952 2959 'DrydockAllocatorWorker' => 'PhabricatorWorker', 2953 2960 'DrydockApacheWebrootInterface' => 'DrydockWebrootInterface', 2961 + 'DrydockBlueprint' => 2962 + array( 2963 + 0 => 'DrydockDAO', 2964 + 1 => 'PhabricatorPolicyInterface', 2965 + ), 2966 + 'DrydockBlueprintCreateController' => 'DrydockController', 2967 + 'DrydockBlueprintEditController' => 'DrydockController', 2968 + 'DrydockBlueprintListController' => 'DrydockController', 2969 + 'DrydockBlueprintQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2970 + 'DrydockBlueprintViewController' => 'DrydockController', 2954 2971 'DrydockCommandInterface' => 'DrydockInterface', 2955 2972 'DrydockController' => 'PhabricatorController', 2956 2973 'DrydockDAO' => 'PhabricatorLiskDAO', ··· 2961 2978 'DrydockLeaseStatus' => 'DrydockConstants', 2962 2979 'DrydockLeaseViewController' => 'DrydockController', 2963 2980 'DrydockLocalCommandInterface' => 'DrydockCommandInterface', 2964 - 'DrydockLocalHostBlueprint' => 'DrydockBlueprint', 2981 + 'DrydockLocalHostBlueprintImplementation' => 'DrydockBlueprintImplementation', 2965 2982 'DrydockLog' => 'DrydockDAO', 2966 2983 'DrydockLogController' => 'DrydockController', 2967 2984 'DrydockLogQuery' => 'PhabricatorOffsetPagedQuery', ··· 2971 2988 'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow', 2972 2989 'DrydockManagementWaitForLeaseWorkflow' => 'DrydockManagementWorkflow', 2973 2990 'DrydockManagementWorkflow' => 'PhutilArgumentWorkflow', 2974 - 'DrydockPreallocatedHostBlueprint' => 'DrydockBlueprint', 2991 + 'DrydockPHIDTypeBlueprint' => 'PhabricatorPHIDType', 2992 + 'DrydockPreallocatedHostBlueprintImplementation' => 'DrydockBlueprintImplementation', 2975 2993 'DrydockResource' => 2976 2994 array( 2977 2995 0 => 'DrydockDAO', ··· 2984 3002 'DrydockResourceViewController' => 'DrydockController', 2985 3003 'DrydockSSHCommandInterface' => 'DrydockCommandInterface', 2986 3004 'DrydockWebrootInterface' => 'DrydockInterface', 2987 - 'DrydockWorkingCopyBlueprint' => 'DrydockBlueprint', 3005 + 'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation', 2988 3006 'FeedPublisherHTTPWorker' => 'FeedPushWorker', 2989 3007 'FeedPublisherWorker' => 'FeedPushWorker', 2990 3008 'FeedPushWorker' => 'PhabricatorWorker',
+6
src/applications/drydock/application/PhabricatorApplicationDrydock.php
··· 34 34 return array( 35 35 '/drydock/' => array( 36 36 '' => 'DrydockResourceListController', 37 + 'blueprint/' => array( 38 + '' => 'DrydockBlueprintListController', 39 + '(?P<id>[1-9]\d*)/' => 'DrydockBlueprintViewController', 40 + 'create/' => 'DrydockBlueprintCreateController', 41 + 'edit/(?P<id>[1-9]\d*)/' => 'DrydockBlueprintEditController', 42 + ), 37 43 'resource/' => array( 38 44 '' => 'DrydockResourceListController', 39 45 '(?P<id>[1-9]\d*)/' => 'DrydockResourceViewController',
+23 -5
src/applications/drydock/blueprint/DrydockBlueprint.php src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
··· 5 5 * @task resource Resource Allocation 6 6 * @task log Logging 7 7 */ 8 - abstract class DrydockBlueprint { 8 + abstract class DrydockBlueprintImplementation { 9 9 10 10 private $activeResource; 11 11 private $activeLease; 12 + private $instance; 12 13 13 14 abstract public function getType(); 14 15 abstract public function getInterface( ··· 17 18 $type); 18 19 19 20 abstract public function isEnabled(); 21 + 22 + abstract public function getDescription(); 20 23 21 24 public function getBlueprintClass() { 22 25 return get_class($this); ··· 35 38 } 36 39 37 40 return $lease; 41 + } 42 + 43 + protected function getInstance() { 44 + if (!$this->instance) { 45 + throw new Exception( 46 + "Attach the blueprint instance to the implementation."); 47 + } 48 + 49 + return $this->instance; 50 + } 51 + 52 + public function attachInstance(DrydockBlueprint $instance) { 53 + $this->instance = $instance; 54 + return $this; 38 55 } 39 56 40 57 ··· 343 360 } 344 361 345 362 346 - public static function getAllBlueprints() { 363 + public static function getAllBlueprintImplementations() { 347 364 static $list = null; 348 365 349 366 if ($list === null) { 350 367 $blueprints = id(new PhutilSymbolLoader()) 351 368 ->setType('class') 352 - ->setAncestorClass('DrydockBlueprint') 369 + ->setAncestorClass('DrydockBlueprintImplementation') 353 370 ->setConcreteOnly(true) 354 371 ->selectAndLoadSymbols(); 355 372 $list = ipull($blueprints, 'name', 'name'); ··· 361 378 return $list; 362 379 } 363 380 364 - public static function getAllBlueprintsForResource($type) { 381 + public static function getAllBlueprintImplementationsForResource($type) { 365 382 static $groups = null; 366 383 if ($groups === null) { 367 - $groups = mgroup(self::getAllBlueprints(), 'getType'); 384 + $groups = mgroup(self::getAllBlueprintImplementations(), 'getType'); 368 385 } 369 386 return idx($groups, $type, array()); 370 387 } 371 388 372 389 protected function newResourceTemplate($name) { 373 390 $resource = new DrydockResource(); 391 + $resource->setBlueprintPHID($this->getInstance()->getPHID()); 374 392 $resource->setBlueprintClass($this->getBlueprintClass()); 375 393 $resource->setType($this->getType()); 376 394 $resource->setStatus(DrydockResourceStatus::STATUS_PENDING);
+6 -1
src/applications/drydock/blueprint/DrydockLocalHostBlueprint.php src/applications/drydock/blueprint/DrydockLocalHostBlueprintImplementation.php
··· 1 1 <?php 2 2 3 - final class DrydockLocalHostBlueprint extends DrydockBlueprint { 3 + final class DrydockLocalHostBlueprintImplementation 4 + extends DrydockBlueprintImplementation { 4 5 5 6 public function isEnabled() { 6 7 return false; 8 + } 9 + 10 + public function getDescription() { 11 + return pht('Allocates storage on the local host.'); 7 12 } 8 13 9 14 public function canAllocateMoreResources(array $pool) {
+6 -1
src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprint.php src/applications/drydock/blueprint/DrydockPreallocatedHostBlueprintImplementation.php
··· 1 1 <?php 2 2 3 - final class DrydockPreallocatedHostBlueprint extends DrydockBlueprint { 3 + final class DrydockPreallocatedHostBlueprintImplementation 4 + extends DrydockBlueprintImplementation { 4 5 5 6 public function isEnabled() { 6 7 return true; 8 + } 9 + 10 + public function getDescription() { 11 + return pht('Leases out preallocated, remote hosts.'); 7 12 } 8 13 9 14 public function canAllocateMoreResources(array $pool) {
+6 -1
src/applications/drydock/blueprint/DrydockWorkingCopyBlueprint.php src/applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php
··· 1 1 <?php 2 2 3 - final class DrydockWorkingCopyBlueprint extends DrydockBlueprint { 3 + final class DrydockWorkingCopyBlueprintImplementation 4 + extends DrydockBlueprintImplementation { 4 5 5 6 public function isEnabled() { 6 7 return true; 8 + } 9 + 10 + public function getDescription() { 11 + return pht('Allocates out working copies of repositories.'); 7 12 } 8 13 9 14 protected function canAllocateLease(
+68
src/applications/drydock/controller/DrydockBlueprintCreateController.php
··· 1 + <?php 2 + 3 + final class DrydockBlueprintCreateController 4 + extends DrydockController { 5 + 6 + public function willProcessRequest(array $data) { 7 + } 8 + 9 + public function processRequest() { 10 + $request = $this->getRequest(); 11 + $viewer = $request->getUser(); 12 + 13 + $implementations = 14 + DrydockBlueprintImplementation::getAllBlueprintImplementations(); 15 + 16 + if ($request->isFormPost()) { 17 + $class = $request->getStr('blueprint-type'); 18 + if (!isset($implementations[$class])) { 19 + return $this->createDialog($implementations); 20 + } 21 + 22 + $blueprint = new DrydockBlueprint(); 23 + $blueprint->setClassName($class); 24 + $blueprint->setDetails(array()); 25 + $blueprint->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN); 26 + $blueprint->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN); 27 + $blueprint->save(); 28 + 29 + $edit_uri = $this->getApplicationURI( 30 + "blueprint/edit/".$blueprint->getID()."/"); 31 + 32 + return id(new AphrontRedirectResponse())->setURI($edit_uri); 33 + } 34 + 35 + return $this->createDialog($implementations); 36 + } 37 + 38 + function createDialog(array $implementations) { 39 + $request = $this->getRequest(); 40 + $viewer = $request->getUser(); 41 + 42 + $control = id(new AphrontFormRadioButtonControl()) 43 + ->setName('blueprint-type'); 44 + 45 + foreach ($implementations as $implementation_name => $implementation) { 46 + $control 47 + ->addButton( 48 + $implementation_name, 49 + $implementation->getBlueprintClass(), 50 + $implementation->getDescription()); 51 + } 52 + 53 + $dialog = new AphrontDialogView(); 54 + $dialog->setTitle(pht('Create New Blueprint')) 55 + ->setUser($viewer) 56 + ->addSubmitButton(pht('Create Blueprint')) 57 + ->addCancelButton($this->getApplicationURI('blueprint/')); 58 + $dialog->appendChild( 59 + phutil_tag( 60 + 'p', 61 + array(), 62 + pht( 63 + 'Select what type of blueprint you want to create: '))); 64 + $dialog->appendChild($control); 65 + return id(new AphrontDialogResponse())->setDialog($dialog); 66 + } 67 + 68 + }
+122
src/applications/drydock/controller/DrydockBlueprintEditController.php
··· 1 + <?php 2 + 3 + final class DrydockBlueprintEditController extends DrydockController { 4 + 5 + private $id; 6 + 7 + public function willProcessRequest(array $data) { 8 + $this->id = idx($data, 'id'); 9 + } 10 + 11 + public function processRequest() { 12 + $request = $this->getRequest(); 13 + $viewer = $request->getUser(); 14 + 15 + if ($this->id) { 16 + $blueprint = id(new DrydockBlueprintQuery()) 17 + ->setViewer($viewer) 18 + ->withIDs(array($this->id)) 19 + ->requireCapabilities( 20 + array( 21 + PhabricatorPolicyCapability::CAN_VIEW, 22 + PhabricatorPolicyCapability::CAN_EDIT, 23 + )) 24 + ->executeOne(); 25 + if (!$blueprint) { 26 + return new Aphront404Response(); 27 + } 28 + } else { 29 + $blueprint = new DrydockBlueprint(); 30 + } 31 + 32 + if ($request->isFormPost()) { 33 + $v_view_policy = $request->getStr('viewPolicy'); 34 + $v_edit_policy = $request->getStr('editPolicy'); 35 + 36 + // TODO: Should we use transactions here? 37 + $blueprint->setViewPolicy($v_view_policy); 38 + $blueprint->setEditPolicy($v_edit_policy); 39 + 40 + $blueprint->save(); 41 + 42 + return id(new AphrontRedirectResponse()) 43 + ->setURI('/drydock/blueprint/'); 44 + } 45 + 46 + $policies = id(new PhabricatorPolicyQuery()) 47 + ->setViewer($viewer) 48 + ->setObject($blueprint) 49 + ->execute(); 50 + 51 + if ($request->isAjax()) { 52 + $form = id(new PHUIFormLayoutView()) 53 + ->setUser($viewer); 54 + } else { 55 + $form = id(new AphrontFormView()) 56 + ->setUser($viewer); 57 + } 58 + 59 + $form 60 + ->appendChild( 61 + id(new AphrontFormTextControl()) 62 + ->setName('className') 63 + ->setLabel(pht('Implementation')) 64 + ->setValue($blueprint->getClassName()) 65 + ->setDisabled(true)) 66 + ->appendChild( 67 + id(new AphrontFormPolicyControl()) 68 + ->setName('viewPolicy') 69 + ->setPolicyObject($blueprint) 70 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 71 + ->setPolicies($policies)) 72 + ->appendChild( 73 + id(new AphrontFormPolicyControl()) 74 + ->setName('editPolicy') 75 + ->setPolicyObject($blueprint) 76 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 77 + ->setPolicies($policies)); 78 + 79 + $crumbs = $this->buildApplicationCrumbs(); 80 + 81 + $title = pht('Edit Blueprint'); 82 + $header = pht('Edit Blueprint %d', $blueprint->getID()); 83 + $crumbs->addCrumb( 84 + id(new PhabricatorCrumbView()) 85 + ->setName(pht('Blueprint %d', $blueprint->getID()))); 86 + $crumbs->addCrumb( 87 + id(new PhabricatorCrumbView()) 88 + ->setName(pht('Edit'))); 89 + 90 + if ($request->isAjax()) { 91 + $dialog = id(new AphrontDialogView()) 92 + ->setUser($viewer) 93 + ->setWidth(AphrontDialogView::WIDTH_FORM) 94 + ->setTitle($title) 95 + ->appendChild($form) 96 + ->addSubmitButton(pht('Edit Blueprint')) 97 + ->addCancelButton($this->getApplicationURI()); 98 + 99 + return id(new AphrontDialogResponse())->setDialog($dialog); 100 + } 101 + 102 + $form->appendChild( 103 + id(new AphrontFormSubmitControl()) 104 + ->setValue(pht('Save')) 105 + ->addCancelButton($this->getApplicationURI())); 106 + 107 + $box = id(new PHUIObjectBoxView()) 108 + ->setHeaderText($header) 109 + ->setForm($form); 110 + 111 + return $this->buildApplicationPage( 112 + array( 113 + $crumbs, 114 + $box, 115 + ), 116 + array( 117 + 'title' => $title, 118 + 'device' => true, 119 + )); 120 + } 121 + 122 + }
+77
src/applications/drydock/controller/DrydockBlueprintListController.php
··· 1 + <?php 2 + 3 + final class DrydockBlueprintListController extends DrydockController { 4 + 5 + public function processRequest() { 6 + $request = $this->getRequest(); 7 + $user = $request->getUser(); 8 + 9 + $title = pht('Blueprints'); 10 + 11 + $blueprint_header = id(new PHUIHeaderView()) 12 + ->setHeader($title); 13 + 14 + $blueprints = id(new DrydockBlueprintQuery()) 15 + ->setViewer($user) 16 + ->execute(); 17 + 18 + $blueprint_list = $this->buildBlueprintListView($blueprints); 19 + 20 + $crumbs = $this->buildApplicationCrumbs(); 21 + $crumbs->addCrumb( 22 + id(new PhabricatorCrumbView()) 23 + ->setName($title) 24 + ->setHref($request->getRequestURI())); 25 + 26 + $crumbs->addAction( 27 + id(new PHUIListItemView()) 28 + ->setName(pht('New Blueprint')) 29 + ->setHref($this->getApplicationURI('blueprint/create/')) 30 + ->setIcon('create')); 31 + 32 + $nav = $this->buildSideNav('blueprint'); 33 + $nav->setCrumbs($crumbs); 34 + $nav->appendChild( 35 + array( 36 + $blueprint_header, 37 + $blueprint_list 38 + )); 39 + 40 + return $this->buildApplicationPage( 41 + $nav, 42 + array( 43 + 'title' => $title, 44 + 'device' => true, 45 + )); 46 + 47 + } 48 + 49 + protected function buildBlueprintListView(array $blueprints) { 50 + assert_instances_of($blueprints, 'DrydockBlueprint'); 51 + 52 + $user = $this->getRequest()->getUser(); 53 + $view = new PHUIObjectItemListView(); 54 + 55 + foreach ($blueprints as $blueprint) { 56 + $item = id(new PHUIObjectItemView()) 57 + ->setHeader($blueprint->getClassName()) 58 + ->setHref($this->getApplicationURI('/blueprint/'.$blueprint->getID())) 59 + ->setObjectName(pht('Blueprint %d', $blueprint->getID())); 60 + 61 + if ($blueprint->getImplementation()->isEnabled()) { 62 + $item->addAttribute(pht('Enabled')); 63 + $item->setBarColor('green'); 64 + } else { 65 + $item->addAttribute(pht('Disabled')); 66 + $item->setBarColor('red'); 67 + } 68 + 69 + $item->addAttribute($blueprint->getImplementation()->getDescription()); 70 + 71 + $view->addItem($item); 72 + } 73 + 74 + return $view; 75 + } 76 + 77 + }
+100
src/applications/drydock/controller/DrydockBlueprintViewController.php
··· 1 + <?php 2 + 3 + final class DrydockBlueprintViewController extends DrydockController { 4 + 5 + private $id; 6 + 7 + public function willProcessRequest(array $data) { 8 + $this->id = $data['id']; 9 + } 10 + 11 + public function processRequest() { 12 + $request = $this->getRequest(); 13 + $user = $request->getUser(); 14 + 15 + $blueprint = id(new DrydockBlueprint())->load($this->id); 16 + if (!$blueprint) { 17 + return new Aphront404Response(); 18 + } 19 + 20 + $title = 'Blueprint '.$blueprint->getID().' '.$blueprint->getClassName(); 21 + 22 + $header = id(new PHUIHeaderView()) 23 + ->setHeader($title); 24 + 25 + $actions = $this->buildActionListView($blueprint); 26 + $properties = $this->buildPropertyListView($blueprint, $actions); 27 + 28 + $blueprint_uri = 'blueprint/'.$blueprint->getID().'/'; 29 + $blueprint_uri = $this->getApplicationURI($blueprint_uri); 30 + 31 + $resources = id(new DrydockResourceQuery()) 32 + ->withBlueprintPHIDs(array($blueprint->getPHID())) 33 + ->setViewer($user) 34 + ->execute(); 35 + 36 + $resource_list = $this->buildResourceListView($resources); 37 + $resource_list->setNoDataString(pht('This blueprint has no resources.')); 38 + 39 + $pager = new AphrontPagerView(); 40 + $pager->setURI(new PhutilURI($blueprint_uri), 'offset'); 41 + $pager->setOffset($request->getInt('offset')); 42 + 43 + $crumbs = $this->buildApplicationCrumbs(); 44 + $crumbs->setActionList($actions); 45 + $crumbs->addCrumb( 46 + id(new PhabricatorCrumbView()) 47 + ->setName(pht('Blueprint %d', $blueprint->getID()))); 48 + 49 + $object_box = id(new PHUIObjectBoxView()) 50 + ->setHeader($header) 51 + ->addPropertyList($properties); 52 + 53 + return $this->buildApplicationPage( 54 + array( 55 + $crumbs, 56 + $object_box, 57 + $resource_list 58 + ), 59 + array( 60 + 'device' => true, 61 + 'title' => $title, 62 + )); 63 + 64 + } 65 + 66 + private function buildActionListView(DrydockBlueprint $blueprint) { 67 + $view = id(new PhabricatorActionListView()) 68 + ->setUser($this->getRequest()->getUser()) 69 + ->setObjectURI($this->getRequest()->getRequestURI()) 70 + ->setObject($blueprint); 71 + 72 + $uri = '/blueprint/edit/'.$blueprint->getID().'/'; 73 + $uri = $this->getApplicationURI($uri); 74 + 75 + $view->addAction( 76 + id(new PhabricatorActionView()) 77 + ->setHref($uri) 78 + ->setName(pht('Edit Blueprint Policies')) 79 + ->setIcon('edit') 80 + ->setWorkflow(true) 81 + ->setDisabled(false)); 82 + 83 + return $view; 84 + } 85 + 86 + private function buildPropertyListView( 87 + DrydockBlueprint $blueprint, 88 + PhabricatorActionListView $actions) { 89 + 90 + $view = new PHUIPropertyListView(); 91 + $view->setActionList($actions); 92 + 93 + $view->addProperty( 94 + pht('Implementation'), 95 + $blueprint->getClassName()); 96 + 97 + return $view; 98 + } 99 + 100 + }
+4 -3
src/applications/drydock/controller/DrydockController.php
··· 5 5 final protected function buildSideNav($selected) { 6 6 $nav = new AphrontSideNavFilterView(); 7 7 $nav->setBaseURI(new PhutilURI('/drydock/')); 8 - $nav->addFilter('resource', 'Resources'); 9 - $nav->addFilter('lease', 'Leases'); 10 - $nav->addFilter('log', 'Logs'); 8 + $nav->addFilter('blueprint', 'Blueprints'); 9 + $nav->addFilter('resource', 'Resources'); 10 + $nav->addFilter('lease', 'Leases'); 11 + $nav->addFilter('log', 'Logs'); 11 12 12 13 $nav->selectFilter($selected, 'resource'); 13 14
+5 -4
src/applications/drydock/controller/DrydockResourceViewController.php
··· 33 33 ->needResources(true) 34 34 ->execute(); 35 35 36 - $lease_header = id(new PHUIHeaderView()) 37 - ->setHeader(pht('Leases')); 38 - 39 36 $lease_list = $this->buildLeaseListView($leases); 40 37 $lease_list->setNoDataString(pht('This resource has no leases.')); 41 38 ··· 64 61 array( 65 62 $crumbs, 66 63 $object_box, 67 - $lease_header, 68 64 $lease_list, 69 65 $log_table, 70 66 ), ··· 113 109 $view->addProperty( 114 110 pht('Resource Type'), 115 111 $resource->getType()); 112 + 113 + // TODO: Load handle. 114 + $view->addProperty( 115 + pht('Blueprint'), 116 + $resource->getBlueprintPHID()); 116 117 117 118 $attributes = $resource->getAttributes(); 118 119 if ($attributes) {
+13 -7
src/applications/drydock/management/DrydockManagementCreateResourceWorkflow.php
··· 16 16 ), 17 17 array( 18 18 'name' => 'blueprint', 19 - 'param' => 'blueprint_type', 20 - 'help' => 'Blueprint type.', 19 + 'param' => 'blueprint_id', 20 + 'help' => 'Blueprint ID.', 21 21 ), 22 22 array( 23 23 'name' => 'attributes', ··· 36 36 "Specify a resource name with `--name`."); 37 37 } 38 38 39 - $blueprint_type = $args->getArg('blueprint'); 40 - if (!$blueprint_type) { 39 + $blueprint_id = $args->getArg('blueprint'); 40 + if (!$blueprint_id) { 41 41 throw new PhutilArgumentUsageException( 42 - "Specify a blueprint type with `--blueprint`."); 42 + "Specify a blueprint ID with `--blueprint`."); 43 43 } 44 44 45 45 $attributes = $args->getArg('attributes'); ··· 49 49 $attributes = $options->parse($attributes); 50 50 } 51 51 52 + $blueprint = id(new DrydockBlueprint())->load((int)$blueprint_id); 53 + if (!$blueprint) { 54 + throw new PhutilArgumentUsageException( 55 + "Specified blueprint does not exist."); 56 + } 57 + 52 58 $resource = new DrydockResource(); 53 - $resource->setBlueprintClass($blueprint_type); 54 - $resource->setType(id(new $blueprint_type())->getType()); 59 + $resource->setBlueprintPHID($blueprint->getPHID()); 60 + $resource->setType($blueprint->getImplementation()->getType()); 55 61 $resource->setName($resource_name); 56 62 $resource->setStatus(DrydockResourceStatus::STATUS_OPEN); 57 63 if ($attributes) {
+34
src/applications/drydock/phid/DrydockPHIDTypeBlueprint.php
··· 1 + <?php 2 + 3 + final class DrydockPHIDTypeBlueprint extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'DRYB'; 6 + 7 + public function getTypeConstant() { 8 + return self::TYPECONST; 9 + } 10 + 11 + public function getTypeName() { 12 + return pht('Blueprint'); 13 + } 14 + 15 + public function newObject() { 16 + return new DrydockBlueprint(); 17 + } 18 + 19 + protected function buildQueryForObjects( 20 + PhabricatorObjectQuery $query, 21 + array $phids) { 22 + 23 + return id(new DrydockBlueprintQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + } 33 + 34 + }
+70
src/applications/drydock/query/DrydockBlueprintQuery.php
··· 1 + <?php 2 + 3 + final class DrydockBlueprintQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + 9 + public function withIDs(array $ids) { 10 + $this->ids = $ids; 11 + return $this; 12 + } 13 + 14 + public function withPHIDs(array $phids) { 15 + $this->phids = $phids; 16 + return $this; 17 + } 18 + 19 + public function loadPage() { 20 + $table = new DrydockBlueprint(); 21 + $conn_r = $table->establishConnection('r'); 22 + 23 + $data = queryfx_all( 24 + $conn_r, 25 + 'SELECT blueprint.* FROM %T blueprint %Q %Q %Q', 26 + $table->getTableName(), 27 + $this->buildWhereClause($conn_r), 28 + $this->buildOrderClause($conn_r), 29 + $this->buildLimitClause($conn_r)); 30 + 31 + $blueprints = $table->loadAllFromArray($data); 32 + 33 + $implementations = 34 + DrydockBlueprintImplementation::getAllBlueprintImplementations(); 35 + 36 + foreach ($blueprints as $blueprint) { 37 + if (array_key_exists($implementations, $blueprint->getClassName())) { 38 + $blueprint->attachImplementation( 39 + $implementations[$blueprint->getClassName()]); 40 + } 41 + } 42 + 43 + return $blueprints; 44 + } 45 + 46 + private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 47 + $where = array(); 48 + 49 + if ($this->ids) { 50 + $where[] = qsprintf( 51 + $conn_r, 52 + 'id IN (%Ld)', 53 + $this->ids); 54 + } 55 + 56 + if ($this->phids) { 57 + $where[] = qsprintf( 58 + $conn_r, 59 + 'phid IN (%Ld)', 60 + $this->phids); 61 + } 62 + 63 + return $this->formatWhereClause($where); 64 + } 65 + 66 + public function getQueryApplicationClass() { 67 + return 'PhabricatorApplicationDrydock'; 68 + } 69 + 70 + }
+5 -1
src/applications/drydock/query/DrydockLeaseQuery.php
··· 40 40 'id IN (%Ld)', 41 41 mpull($leases, 'getResourceID')); 42 42 43 - foreach ($leases as $lease) { 43 + foreach ($leases as $key => $lease) { 44 44 if ($lease->getResourceID()) { 45 45 $resource = idx($resources, $lease->getResourceID()); 46 46 if ($resource) { 47 47 $lease->attachResource($resource); 48 + } else { 49 + unset($leases[$key]); 48 50 } 51 + } else { 52 + unset($leases[$key]); 49 53 } 50 54 } 51 55 }
+13
src/applications/drydock/query/DrydockResourceQuery.php
··· 6 6 private $ids; 7 7 private $statuses; 8 8 private $types; 9 + private $blueprintPHIDs; 9 10 10 11 public function withIDs(array $ids) { 11 12 $this->ids = $ids; ··· 19 20 20 21 public function withStatuses(array $statuses) { 21 22 $this->statuses = $statuses; 23 + return $this; 24 + } 25 + 26 + public function withBlueprintPHIDs(array $blueprint_phids) { 27 + $this->blueprintPHIDs = $blueprint_phids; 22 28 return $this; 23 29 } 24 30 ··· 61 67 $conn_r, 62 68 'status IN (%Ls)', 63 69 $this->statuses); 70 + } 71 + 72 + if ($this->blueprintPHIDs) { 73 + $where[] = qsprintf( 74 + $conn_r, 75 + 'blueprintPHID IN (%Ls)', 76 + $this->blueprintPHIDs); 64 77 } 65 78 66 79 $where[] = $this->buildPagingClause($conn_r);
+72
src/applications/drydock/storage/DrydockBlueprint.php
··· 1 + <?php 2 + 3 + final class DrydockBlueprint extends DrydockDAO 4 + implements PhabricatorPolicyInterface { 5 + 6 + protected $phid; 7 + protected $className; 8 + protected $viewPolicy; 9 + protected $editPolicy; 10 + protected $details; 11 + 12 + public function getConfiguration() { 13 + return array( 14 + self::CONFIG_AUX_PHID => true, 15 + self::CONFIG_SERIALIZATION => array( 16 + 'details' => self::SERIALIZATION_JSON, 17 + ) 18 + ) + parent::getConfiguration(); 19 + } 20 + 21 + public function generatePHID() { 22 + return PhabricatorPHID::generateNewPHID( 23 + DrydockPHIDTypeBlueprint::TYPECONST); 24 + } 25 + 26 + public function getImplementation() { 27 + $class = $this->className; 28 + $implementations = 29 + DrydockBlueprintImplementation::getAllBlueprintImplementations(); 30 + if (!isset($implementations[$class])) { 31 + throw new Exception( 32 + "Invalid class name for blueprint (got '".$class."')"); 33 + } 34 + return id(new $class())->attachInstance($this); 35 + } 36 + 37 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 38 + 39 + 40 + public function getCapabilities() { 41 + return array( 42 + PhabricatorPolicyCapability::CAN_VIEW, 43 + PhabricatorPolicyCapability::CAN_EDIT, 44 + ); 45 + } 46 + 47 + public function getPolicy($capability) { 48 + switch ($capability) { 49 + case PhabricatorPolicyCapability::CAN_VIEW: 50 + return $this->getViewPolicy(); 51 + case PhabricatorPolicyCapability::CAN_EDIT: 52 + return $this->getEditPolicy(); 53 + } 54 + } 55 + 56 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 57 + switch ($capability) { 58 + case PhabricatorPolicyCapability::CAN_VIEW: 59 + case PhabricatorPolicyCapability::CAN_EDIT: 60 + return $viewer->getIsAdmin(); 61 + } 62 + } 63 + 64 + public function describeAutomaticCapability($capability) { 65 + switch ($capability) { 66 + case PhabricatorPolicyCapability::CAN_VIEW: 67 + return pht('Administrators can always view blueprints.'); 68 + case PhabricatorPolicyCapability::CAN_EDIT: 69 + return pht('Administrators can always edit blueprints.'); 70 + } 71 + } 72 + }
+5 -3
src/applications/drydock/storage/DrydockResource.php
··· 5 5 6 6 protected $id; 7 7 protected $phid; 8 - protected $blueprintClass; 8 + protected $blueprintPHID; 9 9 protected $status; 10 10 11 11 protected $type; ··· 50 50 51 51 public function getBlueprint() { 52 52 if (empty($this->blueprint)) { 53 - $this->blueprint = newv($this->blueprintClass, array()); 53 + $blueprint = id(new DrydockBlueprint()) 54 + ->loadOneWhere('phid = %s', $this->blueprintPHID); 55 + $this->blueprint = $blueprint->getImplementation(); 54 56 } 55 57 return $this->blueprint; 56 58 } ··· 76 78 $lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED); 77 79 break; 78 80 } 79 - DrydockBlueprint::writeLog($this, $lease, $message); 81 + DrydockBlueprintImplementation::writeLog($this, $lease, $message); 80 82 $lease->save(); 81 83 } 82 84
+1 -1
src/applications/drydock/util/DrydockBlueprintScopeGuard.php
··· 2 2 3 3 final class DrydockBlueprintScopeGuard { 4 4 5 - public function __construct(DrydockBlueprint $blueprint) { 5 + public function __construct(DrydockBlueprintImplementation $blueprint) { 6 6 $this->blueprint = $blueprint; 7 7 } 8 8
+21 -7
src/applications/drydock/worker/DrydockAllocatorWorker.php
··· 24 24 } 25 25 26 26 private function logToDrydock($message) { 27 - DrydockBlueprint::writeLog( 27 + DrydockBlueprintImplementation::writeLog( 28 28 null, 29 29 $this->loadLease(), 30 30 $message); ··· 52 52 } 53 53 } 54 54 55 + private function loadAllBlueprints() { 56 + $instances = id(new DrydockBlueprint())->loadAll(); 57 + $blueprints = array(); 58 + foreach ($instances as $instance) { 59 + $blueprints[$instance->getPHID()] = $instance; 60 + } 61 + return $blueprints; 62 + } 63 + 55 64 private function allocateLease(DrydockLease $lease) { 56 65 $type = $lease->getResourceType(); 66 + 67 + $blueprints = $this->loadAllBlueprints(); 57 68 58 69 $pool = id(new DrydockResource())->loadAllWhere( 59 70 'type = %s AND status = %s', ··· 65 76 66 77 $candidates = array(); 67 78 foreach ($pool as $key => $candidate) { 68 - try { 69 - $blueprint = $candidate->getBlueprint(); 70 - } catch (Exception $ex) { 79 + if (!isset($blueprints[$candidate->getBlueprintPHID()])) { 71 80 unset($pool[$key]); 72 81 continue; 73 82 } 74 83 75 - if ($blueprint->filterResource($candidate, $lease)) { 84 + $blueprint = $blueprints[$candidate->getBlueprintPHID()]; 85 + $implementation = $blueprint->getImplementation(); 86 + 87 + if ($implementation->filterResource($candidate, $lease)) { 76 88 $candidates[] = $candidate; 77 89 } 78 90 } ··· 83 95 if ($candidates) { 84 96 shuffle($candidates); 85 97 foreach ($candidates as $candidate_resource) { 86 - $blueprint = $candidate_resource->getBlueprint(); 98 + $blueprint = $blueprints[$candidate_resource->getBlueprintPHID()] 99 + ->getImplementation(); 87 100 if ($blueprint->allocateLease($candidate_resource, $lease)) { 88 101 $resource = $candidate_resource; 89 102 break; ··· 92 105 } 93 106 94 107 if (!$resource) { 95 - $blueprints = DrydockBlueprint::getAllBlueprintsForResource($type); 108 + $blueprints = DrydockBlueprintImplementation 109 + ::getAllBlueprintImplementationsForResource($type); 96 110 97 111 $this->logToDrydock( 98 112 pht('Found %d Blueprints', count($blueprints)));
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1788 1788 'type' => 'sql', 1789 1789 'name' => $this->getPatchPath('20131122.repomirror.sql'), 1790 1790 ), 1791 + '20131123.drydockblueprintpolicy.sql' => array( 1792 + 'type' => 'sql', 1793 + 'name' => $this->getPatchPath('20131123.drydockblueprintpolicy.sql'), 1794 + ), 1795 + '20131129.drydockresourceblueprint.sql' => array( 1796 + 'type' => 'sql', 1797 + 'name' => $this->getPatchPath('20131129.drydockresourceblueprint.sql'), 1798 + ), 1791 1799 ); 1792 1800 } 1793 1801 }
+23
src/view/form/PHUIFormLayoutView.php
··· 14 14 return $this; 15 15 } 16 16 17 + public function appendInstructions($text) { 18 + return $this->appendChild( 19 + phutil_tag( 20 + 'div', 21 + array( 22 + 'class' => 'aphront-form-instructions', 23 + ), 24 + $text)); 25 + } 26 + 27 + public function appendRemarkupInstructions($remarkup) { 28 + if ($this->getUser() === null) { 29 + throw new Exception( 30 + "Call `setUser` before appending Remarkup to PHUIFormLayoutView."); 31 + } 32 + 33 + return $this->appendInstructions( 34 + PhabricatorMarkupEngine::renderOneObject( 35 + id(new PhabricatorMarkupOneOff())->setContent($remarkup), 36 + 'default', 37 + $this->getUser())); 38 + } 39 + 17 40 public function render() { 18 41 $classes = array('phui-form-view'); 19 42