Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14466628
D14143.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
47 KB
Referenced Files
None
Subscribers
None
D14143.diff
View Options
diff --git a/resources/sql/autopatches/20150922.drydock.commands.1.sql b/resources/sql/autopatches/20150922.drydock.commands.1.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20150922.drydock.commands.1.sql
@@ -0,0 +1,10 @@
+CREATE TABLE {$NAMESPACE}_drydock.drydock_command (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ authorPHID VARBINARY(64) NOT NULL,
+ targetPHID VARBINARY(64) NOT NULL,
+ command VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
+ isConsumed BOOL NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ KEY `key_target` (targetPHID, isConsumed)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -817,7 +817,9 @@
'DrydockBlueprintTransaction' => 'applications/drydock/storage/DrydockBlueprintTransaction.php',
'DrydockBlueprintTransactionQuery' => 'applications/drydock/query/DrydockBlueprintTransactionQuery.php',
'DrydockBlueprintViewController' => 'applications/drydock/controller/DrydockBlueprintViewController.php',
+ 'DrydockCommand' => 'applications/drydock/storage/DrydockCommand.php',
'DrydockCommandInterface' => 'applications/drydock/interface/command/DrydockCommandInterface.php',
+ 'DrydockCommandQuery' => 'applications/drydock/query/DrydockCommandQuery.php',
'DrydockConsoleController' => 'applications/drydock/controller/DrydockConsoleController.php',
'DrydockConstants' => 'applications/drydock/constants/DrydockConstants.php',
'DrydockController' => 'applications/drydock/controller/DrydockController.php',
@@ -837,6 +839,7 @@
'DrydockLeaseReleaseController' => 'applications/drydock/controller/DrydockLeaseReleaseController.php',
'DrydockLeaseSearchEngine' => 'applications/drydock/query/DrydockLeaseSearchEngine.php',
'DrydockLeaseStatus' => 'applications/drydock/constants/DrydockLeaseStatus.php',
+ 'DrydockLeaseUpdateWorker' => 'applications/drydock/worker/DrydockLeaseUpdateWorker.php',
'DrydockLeaseViewController' => 'applications/drydock/controller/DrydockLeaseViewController.php',
'DrydockLeaseWorker' => 'applications/drydock/worker/DrydockLeaseWorker.php',
'DrydockLog' => 'applications/drydock/storage/DrydockLog.php',
@@ -845,22 +848,25 @@
'DrydockLogListView' => 'applications/drydock/view/DrydockLogListView.php',
'DrydockLogQuery' => 'applications/drydock/query/DrydockLogQuery.php',
'DrydockLogSearchEngine' => 'applications/drydock/query/DrydockLogSearchEngine.php',
- 'DrydockManagementCloseWorkflow' => 'applications/drydock/management/DrydockManagementCloseWorkflow.php',
'DrydockManagementCommandWorkflow' => 'applications/drydock/management/DrydockManagementCommandWorkflow.php',
'DrydockManagementLeaseWorkflow' => 'applications/drydock/management/DrydockManagementLeaseWorkflow.php',
- 'DrydockManagementReleaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseWorkflow.php',
+ 'DrydockManagementReleaseLeaseWorkflow' => 'applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php',
+ 'DrydockManagementReleaseResourceWorkflow' => 'applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php',
+ 'DrydockManagementUpdateLeaseWorkflow' => 'applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php',
+ 'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php',
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
- 'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php',
'DrydockResourceController' => 'applications/drydock/controller/DrydockResourceController.php',
'DrydockResourceDatasource' => 'applications/drydock/typeahead/DrydockResourceDatasource.php',
'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
'DrydockResourceListView' => 'applications/drydock/view/DrydockResourceListView.php',
'DrydockResourcePHIDType' => 'applications/drydock/phid/DrydockResourcePHIDType.php',
'DrydockResourceQuery' => 'applications/drydock/query/DrydockResourceQuery.php',
+ 'DrydockResourceReleaseController' => 'applications/drydock/controller/DrydockResourceReleaseController.php',
'DrydockResourceSearchEngine' => 'applications/drydock/query/DrydockResourceSearchEngine.php',
'DrydockResourceStatus' => 'applications/drydock/constants/DrydockResourceStatus.php',
+ 'DrydockResourceUpdateWorker' => 'applications/drydock/worker/DrydockResourceUpdateWorker.php',
'DrydockResourceViewController' => 'applications/drydock/controller/DrydockResourceViewController.php',
'DrydockResourceWorker' => 'applications/drydock/worker/DrydockResourceWorker.php',
'DrydockSFTPFilesystemInterface' => 'applications/drydock/interface/filesystem/DrydockSFTPFilesystemInterface.php',
@@ -4535,7 +4541,12 @@
'DrydockBlueprintTransaction' => 'PhabricatorApplicationTransaction',
'DrydockBlueprintTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'DrydockBlueprintViewController' => 'DrydockBlueprintController',
+ 'DrydockCommand' => array(
+ 'DrydockDAO',
+ 'PhabricatorPolicyInterface',
+ ),
'DrydockCommandInterface' => 'DrydockInterface',
+ 'DrydockCommandQuery' => 'DrydockQuery',
'DrydockConsoleController' => 'DrydockController',
'DrydockConstants' => 'Phobject',
'DrydockController' => 'PhabricatorController',
@@ -4558,6 +4569,7 @@
'DrydockLeaseReleaseController' => 'DrydockLeaseController',
'DrydockLeaseSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DrydockLeaseStatus' => 'DrydockConstants',
+ 'DrydockLeaseUpdateWorker' => 'DrydockWorker',
'DrydockLeaseViewController' => 'DrydockLeaseController',
'DrydockLeaseWorker' => 'DrydockWorker',
'DrydockLog' => array(
@@ -4569,25 +4581,28 @@
'DrydockLogListView' => 'AphrontView',
'DrydockLogQuery' => 'DrydockQuery',
'DrydockLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
- 'DrydockManagementCloseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementCommandWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementLeaseWorkflow' => 'DrydockManagementWorkflow',
- 'DrydockManagementReleaseWorkflow' => 'DrydockManagementWorkflow',
+ 'DrydockManagementReleaseLeaseWorkflow' => 'DrydockManagementWorkflow',
+ 'DrydockManagementReleaseResourceWorkflow' => 'DrydockManagementWorkflow',
+ 'DrydockManagementUpdateLeaseWorkflow' => 'DrydockManagementWorkflow',
+ 'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DrydockResource' => array(
'DrydockDAO',
'PhabricatorPolicyInterface',
),
- 'DrydockResourceCloseController' => 'DrydockResourceController',
'DrydockResourceController' => 'DrydockController',
'DrydockResourceDatasource' => 'PhabricatorTypeaheadDatasource',
'DrydockResourceListController' => 'DrydockResourceController',
'DrydockResourceListView' => 'AphrontView',
'DrydockResourcePHIDType' => 'PhabricatorPHIDType',
'DrydockResourceQuery' => 'DrydockQuery',
+ 'DrydockResourceReleaseController' => 'DrydockResourceController',
'DrydockResourceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DrydockResourceStatus' => 'DrydockConstants',
+ 'DrydockResourceUpdateWorker' => 'DrydockWorker',
'DrydockResourceViewController' => 'DrydockResourceController',
'DrydockResourceWorker' => 'DrydockWorker',
'DrydockSFTPFilesystemInterface' => 'DrydockFilesystemInterface',
diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php
--- a/src/applications/drydock/application/PhabricatorDrydockApplication.php
+++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php
@@ -55,8 +55,10 @@
),
'resource/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController',
- '(?P<id>[1-9]\d*)/' => 'DrydockResourceViewController',
- '(?P<id>[1-9]\d*)/close/' => 'DrydockResourceCloseController',
+ '(?P<id>[1-9]\d*)/' => array(
+ '' => 'DrydockResourceViewController',
+ 'release/' => 'DrydockResourceReleaseController',
+ ),
),
'lease/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLeaseListController',
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
@@ -36,4 +36,53 @@
->addRawContent($table);
}
+ protected function buildCommandsTab($target_phid) {
+ $viewer = $this->getViewer();
+
+ $commands = id(new DrydockCommandQuery())
+ ->setViewer($viewer)
+ ->withTargetPHIDs(array($target_phid))
+ ->execute();
+
+ $consumed_yes = id(new PHUIIconView())
+ ->setIconFont('fa-check green');
+ $consumed_no = id(new PHUIIconView())
+ ->setIconFont('fa-clock-o grey');
+
+ $rows = array();
+ foreach ($commands as $command) {
+ $rows[] = array(
+ $command->getID(),
+ $viewer->renderHandle($command->getAuthorPHID()),
+ $command->getCommand(),
+ ($command->getIsConsumed()
+ ? $consumed_yes
+ : $consumed_no),
+ phabricator_datetime($command->getDateCreated(), $viewer),
+ );
+ }
+
+ $table = id(new AphrontTableView($rows))
+ ->setNoDataString(pht('No commands issued.'))
+ ->setHeaders(
+ array(
+ pht('ID'),
+ pht('From'),
+ pht('Command'),
+ null,
+ pht('Date'),
+ ))
+ ->setColumnClasses(
+ array(
+ null,
+ null,
+ 'wide',
+ null,
+ null,
+ ));
+
+ return id(new PHUIPropertyListView())
+ ->addRawContent($table);
+ }
+
}
diff --git a/src/applications/drydock/controller/DrydockLeaseReleaseController.php b/src/applications/drydock/controller/DrydockLeaseReleaseController.php
--- a/src/applications/drydock/controller/DrydockLeaseReleaseController.php
+++ b/src/applications/drydock/controller/DrydockLeaseReleaseController.php
@@ -9,6 +9,11 @@
$lease = id(new DrydockLeaseQuery())
->setViewer($viewer)
->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
->executeOne();
if (!$lease) {
return new Aphront404Response();
@@ -17,43 +22,35 @@
$lease_uri = '/lease/'.$lease->getID().'/';
$lease_uri = $this->getApplicationURI($lease_uri);
- if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
- ->setTitle(pht('Lease Not Active'))
- ->appendChild(
- phutil_tag(
- 'p',
- array(),
- pht('You can only release "active" leases.')))
+ if (!$lease->canRelease()) {
+ return $this->newDialog()
+ ->setTitle(pht('Lease Not Releasable'))
+ ->appendParagraph(
+ pht(
+ 'Leases can not be released after they are destroyed.'))
->addCancelButton($lease_uri);
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
}
- if (!$request->isDialogFormPost()) {
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
- ->setTitle(pht('Really release lease?'))
- ->appendChild(
- phutil_tag(
- 'p',
- array(),
- pht(
- 'Releasing a lease may cause trouble for the lease holder and '.
- 'trigger cleanup of the underlying resource. It can not be '.
- 'undone. Continue?')))
- ->addSubmitButton(pht('Release Lease'))
- ->addCancelButton($lease_uri);
+ if ($request->isFormPost()) {
+ $command = DrydockCommand::initializeNewCommand($viewer)
+ ->setTargetPHID($lease->getPHID())
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
- return id(new AphrontDialogResponse())->setDialog($dialog);
- }
+ $lease->scheduleUpdate();
- $resource = $lease->getResource();
- $blueprint = $resource->getBlueprint();
- $blueprint->releaseLease($resource, $lease);
+ return id(new AphrontRedirectResponse())->setURI($lease_uri);
+ }
- return id(new AphrontReloadResponse())->setURI($lease_uri);
+ return $this->newDialog()
+ ->setTitle(pht('Release Lease?'))
+ ->appendParagraph(
+ pht(
+ 'Forcefully releasing a lease may interfere with the operation '.
+ 'of the lease holder and trigger destruction of the underlying '.
+ 'resource. It can not be undone.'))
+ ->addSubmitButton(pht('Release Lease'))
+ ->addCancelButton($lease_uri);
}
}
diff --git a/src/applications/drydock/controller/DrydockLeaseViewController.php b/src/applications/drydock/controller/DrydockLeaseViewController.php
--- a/src/applications/drydock/controller/DrydockLeaseViewController.php
+++ b/src/applications/drydock/controller/DrydockLeaseViewController.php
@@ -43,11 +43,13 @@
$crumbs->addTextCrumb($title, $lease_uri);
$locks = $this->buildLocksTab($lease->getPHID());
+ $commands = $this->buildCommandsTab($lease->getPHID());
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties, pht('Properties'))
- ->addPropertyList($locks, pht('Slot Locks'));
+ ->addPropertyList($locks, pht('Slot Locks'))
+ ->addPropertyList($commands, pht('Commands'));
$log_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Lease Logs'))
@@ -66,14 +68,20 @@
}
private function buildActionListView(DrydockLease $lease) {
+ $viewer = $this->getViewer();
+
$view = id(new PhabricatorActionListView())
- ->setUser($this->getRequest()->getUser())
+ ->setUser($viewer)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($lease);
$id = $lease->getID();
- $can_release = ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACTIVE);
+ $can_release = $lease->canRelease();
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $lease,
+ PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
@@ -81,7 +89,7 @@
->setIcon('fa-times')
->setHref($this->getApplicationURI("/lease/{$id}/release/"))
->setWorkflow(true)
- ->setDisabled(!$can_release));
+ ->setDisabled(!$can_release || !$can_edit));
return $view;
}
diff --git a/src/applications/drydock/controller/DrydockResourceCloseController.php b/src/applications/drydock/controller/DrydockResourceCloseController.php
deleted file mode 100644
--- a/src/applications/drydock/controller/DrydockResourceCloseController.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-final class DrydockResourceCloseController extends DrydockResourceController {
-
- public function handleRequest(AphrontRequest $request) {
- $viewer = $request->getViewer();
- $id = $request->getURIData('id');
-
- $resource = id(new DrydockResourceQuery())
- ->setViewer($viewer)
- ->withIDs(array($id))
- ->executeOne();
- if (!$resource) {
- return new Aphront404Response();
- }
-
- $resource_uri = '/resource/'.$resource->getID().'/';
- $resource_uri = $this->getApplicationURI($resource_uri);
-
- if ($resource->getStatus() != DrydockResourceStatus::STATUS_OPEN) {
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
- ->setTitle(pht('Resource Not Open'))
- ->appendChild(phutil_tag('p', array(), pht(
- 'You can only close "open" resources.')))
- ->addCancelButton($resource_uri);
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
- }
-
- if ($request->isFormPost()) {
- $resource->closeResource();
- return id(new AphrontReloadResponse())->setURI($resource_uri);
- }
-
- $dialog = id(new AphrontDialogView())
- ->setUser($viewer)
- ->setTitle(pht('Really close resource?'))
- ->appendChild(
- pht(
- 'Closing a resource releases all leases and destroys the '.
- 'resource. It can not be undone. Continue?'))
- ->addSubmitButton(pht('Close Resource'))
- ->addCancelButton($resource_uri);
-
- return id(new AphrontDialogResponse())->setDialog($dialog);
- }
-
-}
diff --git a/src/applications/drydock/controller/DrydockResourceReleaseController.php b/src/applications/drydock/controller/DrydockResourceReleaseController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/controller/DrydockResourceReleaseController.php
@@ -0,0 +1,56 @@
+<?php
+
+final class DrydockResourceReleaseController extends DrydockResourceController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+
+ $resource = id(new DrydockResourceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->requireCapabilities(
+ array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
+ ))
+ ->executeOne();
+ if (!$resource) {
+ return new Aphront404Response();
+ }
+
+ $resource_uri = '/resource/'.$resource->getID().'/';
+ $resource_uri = $this->getApplicationURI($resource_uri);
+
+ if (!$resource->canRelease()) {
+ return $this->newDialog()
+ ->setTitle(pht('Resource Not Releasable'))
+ ->appendParagraph(
+ pht(
+ 'Resources can not be released after they are destroyed.'))
+ ->addCancelButton($resource_uri);
+ }
+
+ if ($request->isFormPost()) {
+ $command = DrydockCommand::initializeNewCommand($viewer)
+ ->setTargetPHID($resource->getPHID())
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
+
+ $resource->scheduleUpdate();
+
+ return id(new AphrontRedirectResponse())->setURI($resource_uri);
+ }
+
+
+ return $this->newDialog()
+ ->setTitle(pht('Really release resource?'))
+ ->appendChild(
+ pht(
+ 'Releasing a resource releases all leases and destroys the '.
+ 'resource. It can not be undone.'))
+ ->addSubmitButton(pht('Release Resource'))
+ ->addCancelButton($resource_uri);
+ }
+
+}
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
@@ -55,11 +55,13 @@
$crumbs->addTextCrumb(pht('Resource %d', $resource->getID()));
$locks = $this->buildLocksTab($resource->getPHID());
+ $commands = $this->buildCommandsTab($resource->getPHID());
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties, pht('Properties'))
- ->addPropertyList($locks, pht('Slot Locks'));
+ ->addPropertyList($locks, pht('Slot Locks'))
+ ->addPropertyList($commands, pht('Commands'));
$lease_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Leases'))
@@ -83,22 +85,29 @@
}
private function buildActionListView(DrydockResource $resource) {
+ $viewer = $this->getViewer();
+
$view = id(new PhabricatorActionListView())
- ->setUser($this->getRequest()->getUser())
+ ->setUser($viewer)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($resource);
- $can_close = ($resource->getStatus() == DrydockResourceStatus::STATUS_OPEN);
- $uri = '/resource/'.$resource->getID().'/close/';
+ $can_release = $resource->canRelease();
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $resource,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ $uri = '/resource/'.$resource->getID().'/release/';
$uri = $this->getApplicationURI($uri);
$view->addAction(
id(new PhabricatorActionView())
->setHref($uri)
- ->setName(pht('Close Resource'))
+ ->setName(pht('Release Resource'))
->setIcon('fa-times')
->setWorkflow(true)
- ->setDisabled(!$can_close));
+ ->setDisabled(!$can_release || !$can_edit));
return $view;
}
diff --git a/src/applications/drydock/management/DrydockManagementCloseWorkflow.php b/src/applications/drydock/management/DrydockManagementCloseWorkflow.php
deleted file mode 100644
--- a/src/applications/drydock/management/DrydockManagementCloseWorkflow.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-final class DrydockManagementCloseWorkflow
- extends DrydockManagementWorkflow {
-
- protected function didConstruct() {
- $this
- ->setName('close')
- ->setSynopsis(pht('Close a resource.'))
- ->setArguments(
- array(
- array(
- 'name' => 'ids',
- 'wildcard' => true,
- ),
- ));
- }
-
- public function execute(PhutilArgumentParser $args) {
- $console = PhutilConsole::getConsole();
-
- $ids = $args->getArg('ids');
- if (!$ids) {
- throw new PhutilArgumentUsageException(
- pht('Specify one or more resource IDs to close.'));
- }
-
- $viewer = $this->getViewer();
-
- $resources = id(new DrydockResourceQuery())
- ->setViewer($viewer)
- ->withIDs($ids)
- ->execute();
-
- foreach ($ids as $id) {
- $resource = idx($resources, $id);
- if (!$resource) {
- $console->writeErr("%s\n", pht('Resource %d does not exist!', $id));
- } else if ($resource->getStatus() != DrydockResourceStatus::STATUS_OPEN) {
- $console->writeErr("%s\n", pht("Resource %d is not 'open'!", $id));
- } else {
- $resource->closeResource();
- $console->writeErr("%s\n", pht('Closed resource %d.', $id));
- }
- }
-
- }
-
-}
diff --git a/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/management/DrydockManagementReleaseLeaseWorkflow.php
@@ -0,0 +1,70 @@
+<?php
+
+final class DrydockManagementReleaseLeaseWorkflow
+ extends DrydockManagementWorkflow {
+
+ protected function didConstruct() {
+ $this
+ ->setName('release-lease')
+ ->setSynopsis(pht('Release a lease.'))
+ ->setArguments(
+ array(
+ array(
+ 'name' => 'id',
+ 'param' => 'id',
+ 'repeat' => true,
+ 'help' => pht('Lease ID to release.'),
+ ),
+ ));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $ids = $args->getArg('id');
+ if (!$ids) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify one or more lease IDs to release with "%s".',
+ '--id'));
+ }
+
+ $viewer = $this->getViewer();
+ $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
+
+ $leases = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
+ ->withIDs($ids)
+ ->execute();
+
+ PhabricatorWorker::setRunAllTasksInProcess(true);
+ foreach ($ids as $id) {
+ $lease = idx($leases, $id);
+ if (!$lease) {
+ echo tsprintf(
+ "%s\n",
+ pht('Lease "%s" does not exist.', $id));
+ continue;
+ }
+
+ if (!$lease->canRelease()) {
+ echo tsprintf(
+ "%s\n",
+ pht('Lease "%s" is not releasable.', $id));
+ continue;
+ }
+
+ $command = DrydockCommand::initializeNewCommand($viewer)
+ ->setTargetPHID($lease->getPHID())
+ ->setAuthorPHID($drydock_phid)
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
+
+ $lease->scheduleUpdate();
+
+ echo tsprintf(
+ "%s\n",
+ pht('Scheduled release of lease "%s".', $id));
+ }
+
+ }
+
+}
diff --git a/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/management/DrydockManagementReleaseResourceWorkflow.php
@@ -0,0 +1,71 @@
+<?php
+
+final class DrydockManagementReleaseResourceWorkflow
+ extends DrydockManagementWorkflow {
+
+ protected function didConstruct() {
+ $this
+ ->setName('release-resource')
+ ->setSynopsis(pht('Release a resource.'))
+ ->setArguments(
+ array(
+ array(
+ 'name' => 'id',
+ 'param' => 'id',
+ 'repeat' => true,
+ 'help' => pht('Resource ID to release.'),
+ ),
+ ));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $ids = $args->getArg('id');
+ if (!$ids) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify one or more resource IDs to release with "%s".',
+ '--id'));
+ }
+
+ $viewer = $this->getViewer();
+ $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
+
+ $resources = id(new DrydockResourceQuery())
+ ->setViewer($viewer)
+ ->withIDs($ids)
+ ->execute();
+
+ PhabricatorWorker::setRunAllTasksInProcess(true);
+ foreach ($ids as $id) {
+ $resource = idx($resources, $id);
+
+ if (!$resource) {
+ echo tsprintf(
+ "%s\n",
+ pht('Resource "%s" does not exist.', $id));
+ continue;
+ }
+
+ if (!$resource->canRelease()) {
+ echo tsprintf(
+ "%s\n",
+ pht('Resource "%s" is not releasable.', $id));
+ continue;
+ }
+
+ $command = DrydockCommand::initializeNewCommand($viewer)
+ ->setTargetPHID($resource->getPHID())
+ ->setAuthorPHID($drydock_phid)
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
+
+ $resource->scheduleUpdate();
+
+ echo tsprintf(
+ "%s\n",
+ pht('Scheduled release of resource "%s".', $id));
+ }
+
+ }
+
+}
diff --git a/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
deleted file mode 100644
--- a/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-final class DrydockManagementReleaseWorkflow
- extends DrydockManagementWorkflow {
-
- protected function didConstruct() {
- $this
- ->setName('release')
- ->setSynopsis(pht('Release a lease.'))
- ->setArguments(
- array(
- array(
- 'name' => 'ids',
- 'wildcard' => true,
- ),
- ));
- }
-
- public function execute(PhutilArgumentParser $args) {
- $console = PhutilConsole::getConsole();
-
- $ids = $args->getArg('ids');
- if (!$ids) {
- throw new PhutilArgumentUsageException(
- pht('Specify one or more lease IDs to release.'));
- }
-
- $viewer = $this->getViewer();
-
- $leases = id(new DrydockLeaseQuery())
- ->setViewer($viewer)
- ->withIDs($ids)
- ->execute();
-
- foreach ($ids as $id) {
- $lease = idx($leases, $id);
- if (!$lease) {
- $console->writeErr("%s\n", pht('Lease %d does not exist!', $id));
- } else if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
- $console->writeErr("%s\n", pht("Lease %d is not 'active'!", $id));
- } else {
- $resource = $lease->getResource();
- $blueprint = $resource->getBlueprint();
- $blueprint->releaseLease($resource, $lease);
-
- $console->writeErr("%s\n", pht('Released lease %d.', $id));
- }
- }
-
- }
-
-}
diff --git a/src/applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php b/src/applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php
@@ -0,0 +1,57 @@
+<?php
+
+final class DrydockManagementUpdateLeaseWorkflow
+ extends DrydockManagementWorkflow {
+
+ protected function didConstruct() {
+ $this
+ ->setName('update-lease')
+ ->setSynopsis(pht('Update a lease.'))
+ ->setArguments(
+ array(
+ array(
+ 'name' => 'id',
+ 'param' => 'id',
+ 'repeat' => true,
+ 'help' => pht('Lease ID to update.'),
+ ),
+ ));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $viewer = $this->getViewer();
+
+ $ids = $args->getArg('id');
+ if (!$ids) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify one or more lease IDs to update with "%s".',
+ '--id'));
+ }
+
+ $leases = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
+ ->withIDs($ids)
+ ->execute();
+
+ PhabricatorWorker::setRunAllTasksInProcess(true);
+
+ foreach ($ids as $id) {
+ $lease = idx($leases, $id);
+
+ if (!$lease) {
+ echo tsprintf(
+ "%s\n",
+ pht('Lease "%s" does not exist.', $id));
+ continue;
+ }
+
+ echo tsprintf(
+ "%s\n",
+ pht('Updating lease "%s".', $id));
+
+ $lease->scheduleUpdate();
+ }
+ }
+
+}
diff --git a/src/applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php b/src/applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php
@@ -0,0 +1,58 @@
+<?php
+
+final class DrydockManagementUpdateResourceWorkflow
+ extends DrydockManagementWorkflow {
+
+ protected function didConstruct() {
+ $this
+ ->setName('update-resource')
+ ->setSynopsis(pht('Update a resource.'))
+ ->setArguments(
+ array(
+ array(
+ 'name' => 'id',
+ 'param' => 'id',
+ 'repeat' => true,
+ 'help' => pht('Resource ID to update.'),
+ ),
+ ));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $viewer = $this->getViewer();
+
+ $ids = $args->getArg('id');
+ if (!$ids) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify one or more resource IDs to update with "%s".',
+ '--id'));
+ }
+
+ $resources = id(new DrydockResourceQuery())
+ ->setViewer($viewer)
+ ->withIDs($ids)
+ ->execute();
+
+ PhabricatorWorker::setRunAllTasksInProcess(true);
+
+ foreach ($ids as $id) {
+ $resource = idx($resources, $id);
+
+ if (!$resource) {
+ echo tsprintf(
+ "%s\n",
+ pht('Resource "%s" does not exist.', $id));
+ continue;
+ }
+
+ echo tsprintf(
+ "%s\n",
+ pht('Updating resource "%s".', $id));
+
+ $resource->scheduleUpdate();
+ }
+
+ }
+
+}
diff --git a/src/applications/drydock/query/DrydockCommandQuery.php b/src/applications/drydock/query/DrydockCommandQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/query/DrydockCommandQuery.php
@@ -0,0 +1,82 @@
+<?php
+
+final class DrydockCommandQuery extends DrydockQuery {
+
+ private $ids;
+ private $targetPHIDs;
+ private $consumed;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withTargetPHIDs(array $phids) {
+ $this->targetPHIDs = $phids;
+ return $this;
+ }
+
+ public function withConsumed($consumed) {
+ $this->consumed = $consumed;
+ return $this;
+ }
+
+ public function newResultObject() {
+ return new DrydockCommand();
+ }
+
+ protected function loadPage() {
+ return $this->loadStandardPage($this->newResultObject());
+ }
+
+ protected function willFilterPage(array $commands) {
+ $target_phids = mpull($commands, 'getTargetPHID');
+
+ $targets = id(new PhabricatorObjectQuery())
+ ->setViewer($this->getViewer())
+ ->setParentQuery($this)
+ ->withPHIDs($target_phids)
+ ->execute();
+ $targets = mpull($targets, null, 'getPHID');
+
+ foreach ($commands as $key => $command) {
+ $target = idx($targets, $command->getTargetPHID());
+ if (!$target) {
+ $this->didRejectResult($command);
+ unset($commands[$key]);
+ continue;
+ }
+ $command->attachCommandTarget($target);
+ }
+
+ return $commands;
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->targetPHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'targetPHID IN (%Ls)',
+ $this->targetPHIDs);
+ }
+
+ if ($this->consumed !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'isConsumed = %d',
+ (int)$this->consumed);
+ }
+
+ return $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
@@ -7,6 +7,7 @@
private $resourceIDs;
private $statuses;
private $datasourceQuery;
+ private $needCommands;
public function withIDs(array $ids) {
$this->ids = $ids;
diff --git a/src/applications/drydock/storage/DrydockCommand.php b/src/applications/drydock/storage/DrydockCommand.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/storage/DrydockCommand.php
@@ -0,0 +1,69 @@
+<?php
+
+final class DrydockCommand
+ extends DrydockDAO
+ implements PhabricatorPolicyInterface {
+
+ const COMMAND_RELEASE = 'release';
+
+ protected $authorPHID;
+ protected $targetPHID;
+ protected $command;
+ protected $isConsumed;
+
+ private $commandTarget = self::ATTACHABLE;
+
+ public static function initializeNewCommand(PhabricatorUser $author) {
+ return id(new DrydockCommand())
+ ->setAuthorPHID($author->getPHID())
+ ->setIsConsumed(0);
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'command' => 'text32',
+ 'isConsumed' => 'bool',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_target' => array(
+ 'columns' => array('targetPHID', 'isConsumed'),
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function attachCommandTarget($target) {
+ $this->commandTarget = $target;
+ return $this;
+ }
+
+ public function getCommandTarget() {
+ return $this->assertAttached($this->commandTarget);
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getCommandTarget()->getPolicy($capability);
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return $this->getCommandTarget()->hasAutomaticCapability(
+ $capability,
+ $viewer);
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return pht('Drydock commands have the same policies as their targets.');
+ }
+
+}
diff --git a/src/applications/drydock/storage/DrydockLease.php b/src/applications/drydock/storage/DrydockLease.php
--- a/src/applications/drydock/storage/DrydockLease.php
+++ b/src/applications/drydock/storage/DrydockLease.php
@@ -30,11 +30,24 @@
}
public function __destruct() {
- if ($this->releaseOnDestruction) {
- if ($this->isActive()) {
- $this->release();
- }
+ if (!$this->releaseOnDestruction) {
+ return;
+ }
+
+ if (!$this->canRelease()) {
+ return;
}
+
+ $actor = PhabricatorUser::getOmnipotentUser();
+ $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
+
+ $command = DrydockCommand::initializeNewCommand($actor)
+ ->setTargetPHID($this->getPHID())
+ ->setAuthorPHID($drydock_phid)
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
+
+ $this->scheduleUpdate();
}
public function getLeaseName() {
@@ -130,18 +143,6 @@
return $this;
}
- public function release() {
- $this->assertActive();
- $this->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
- $this->save();
-
- DrydockSlotLock::releaseLocks($this->getPHID());
-
- $this->resource = null;
-
- return $this;
- }
-
public function isActive() {
switch ($this->status) {
case DrydockLeaseStatus::STATUS_ACQUIRED:
@@ -262,6 +263,10 @@
$this->isAcquired = true;
+ if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) {
+ $this->didActivate();
+ }
+
return $this;
}
@@ -301,6 +306,8 @@
$this->isActivated = true;
+ $this->didActivate();
+
return $this;
}
@@ -308,6 +315,48 @@
return $this->isActivated;
}
+ public function canRelease() {
+ if (!$this->getID()) {
+ return false;
+ }
+
+ switch ($this->getStatus()) {
+ case DrydockLeaseStatus::STATUS_RELEASED:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ public function scheduleUpdate() {
+ PhabricatorWorker::scheduleTask(
+ 'DrydockLeaseUpdateWorker',
+ array(
+ 'leasePHID' => $this->getPHID(),
+ ),
+ array(
+ 'objectPHID' => $this->getPHID(),
+ ));
+ }
+
+ private function didActivate() {
+ $viewer = PhabricatorUser::getOmnipotentUser();
+ $need_update = false;
+
+ $commands = id(new DrydockCommandQuery())
+ ->setViewer($viewer)
+ ->withTargetPHIDs(array($this->getPHID()))
+ ->withConsumed(false)
+ ->execute();
+ if ($commands) {
+ $need_update = true;
+ }
+
+ if ($need_update) {
+ $this->scheduleUpdate();
+ }
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
@@ -315,6 +364,7 @@
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
);
}
@@ -322,6 +372,9 @@
if ($this->getResource()) {
return $this->getResource()->getPolicy($capability);
}
+
+ // TODO: Implement reasonable policies.
+
return PhabricatorPolicies::getMostOpenPolicy();
}
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
@@ -170,43 +170,44 @@
return $this->isActivated;
}
- public function closeResource() {
+ public function canRelease() {
+ switch ($this->getStatus()) {
+ case DrydockResourceStatus::STATUS_CLOSED:
+ case DrydockResourceStatus::STATUS_DESTROYED:
+ return false;
+ default:
+ return true;
+ }
+ }
- // TODO: This is super broken and will race other lease writers!
+ public function scheduleUpdate() {
+ PhabricatorWorker::scheduleTask(
+ 'DrydockResourceUpdateWorker',
+ array(
+ 'resourcePHID' => $this->getPHID(),
+ ),
+ array(
+ 'objectPHID' => $this->getPHID(),
+ ));
+ }
- $this->openTransaction();
- $statuses = array(
- DrydockLeaseStatus::STATUS_PENDING,
- DrydockLeaseStatus::STATUS_ACTIVE,
- );
-
- $leases = id(new DrydockLeaseQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withResourceIDs(array($this->getID()))
- ->withStatuses($statuses)
- ->execute();
-
- foreach ($leases as $lease) {
- switch ($lease->getStatus()) {
- case DrydockLeaseStatus::STATUS_PENDING:
- $message = pht('Breaking pending lease (resource closing).');
- $lease->setStatus(DrydockLeaseStatus::STATUS_BROKEN);
- break;
- case DrydockLeaseStatus::STATUS_ACTIVE:
- $message = pht('Releasing active lease (resource closing).');
- $lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
- break;
- }
- DrydockBlueprintImplementation::writeLog($this, $lease, $message);
- $lease->save();
- }
-
- $this->setStatus(DrydockResourceStatus::STATUS_CLOSED);
- $this->save();
-
- DrydockSlotLock::releaseLocks($this->getPHID());
+ private function didActivate() {
+ $viewer = PhabricatorUser::getOmnipotentUser();
- $this->saveTransaction();
+ $need_update = false;
+
+ $commands = id(new DrydockCommandQuery())
+ ->setViewer($viewer)
+ ->withTargetPHIDs(array($this->getPHID()))
+ ->withConsumed(false)
+ ->execute();
+ if ($commands) {
+ $need_update = true;
+ }
+
+ if ($need_update) {
+ $this->scheduleUpdate();
+ }
}
@@ -216,12 +217,15 @@
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
+ PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
+ case PhabricatorPolicyCapability::CAN_EDIT:
+ // TODO: Implement reasonable policies.
return PhabricatorPolicies::getMostOpenPolicy();
}
}
diff --git a/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/worker/DrydockLeaseUpdateWorker.php
@@ -0,0 +1,60 @@
+<?php
+
+final class DrydockLeaseUpdateWorker extends DrydockWorker {
+
+ protected function doWork() {
+ $lease_phid = $this->getTaskDataValue('leasePHID');
+
+ $hash = PhabricatorHash::digestForIndex($lease_phid);
+ $lock_key = 'drydock.lease:'.$hash;
+
+ $lock = PhabricatorGlobalLock::newLock($lock_key)
+ ->lock(1);
+
+ $lease = $this->loadLease($lease_phid);
+ $this->updateLease($lease);
+
+ $lock->unlock();
+ }
+
+ private function updateLease(DrydockLease $lease) {
+ $commands = $this->loadCommands($lease->getPHID());
+ foreach ($commands as $command) {
+ if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
+ // Leases can't receive commands before they activate or after they
+ // release.
+ break;
+ }
+
+ $this->processCommand($lease, $command);
+ $command
+ ->setIsConsumed(true)
+ ->save();
+ }
+ }
+
+ private function processCommand(
+ DrydockLease $lease,
+ DrydockCommand $command) {
+ switch ($command->getCommand()) {
+ case DrydockCommand::COMMAND_RELEASE:
+ $this->releaseLease($lease);
+ break;
+ }
+ }
+
+ private function releaseLease(DrydockLease $lease) {
+ $lease->openTransaction();
+ $lease
+ ->setStatus(DrydockLeaseStatus::STATUS_RELEASED)
+ ->save();
+
+ // TODO: Hold slot locks until destruction?
+ DrydockSlotLock::releaseLocks($lease->getPHID());
+ $lease->saveTransaction();
+
+ // TODO: Hook for resource release behaviors.
+ // TODO: Schedule lease destruction.
+ }
+
+}
diff --git a/src/applications/drydock/worker/DrydockResourceUpdateWorker.php b/src/applications/drydock/worker/DrydockResourceUpdateWorker.php
new file mode 100644
--- /dev/null
+++ b/src/applications/drydock/worker/DrydockResourceUpdateWorker.php
@@ -0,0 +1,92 @@
+<?php
+
+final class DrydockResourceUpdateWorker extends DrydockWorker {
+
+ protected function doWork() {
+ $resource_phid = $this->getTaskDataValue('resourcePHID');
+
+ $hash = PhabricatorHash::digestForIndex($resource_phid);
+ $lock_key = 'drydock.resource:'.$hash;
+
+ $lock = PhabricatorGlobalLock::newLock($lock_key)
+ ->lock(1);
+
+ $resource = $this->loadResource($resource_phid);
+ $this->updateResource($resource);
+
+ $lock->unlock();
+ }
+
+ private function updateResource(DrydockResource $resource) {
+ $commands = $this->loadCommands($resource->getPHID());
+ foreach ($commands as $command) {
+ if ($resource->getStatus() != DrydockResourceStatus::STATUS_OPEN) {
+ // Resources can't receive commands before they activate or after they
+ // release.
+ break;
+ }
+
+ $this->processCommand($resource, $command);
+
+ $command
+ ->setIsConsumed(true)
+ ->save();
+ }
+ }
+
+ private function processCommand(
+ DrydockResource $resource,
+ DrydockCommand $command) {
+
+ switch ($command->getCommand()) {
+ case DrydockCommand::COMMAND_RELEASE:
+ $this->releaseResource($resource);
+ break;
+ }
+ }
+
+ private function releaseResource(DrydockResource $resource) {
+ if ($resource->getStatus() != DrydockResourceStatus::STATUS_OPEN) {
+ // If we had multiple release commands
+ // This command is only meaningful to resources in the "Open" state.
+ return;
+ }
+
+ $viewer = $this->getViewer();
+ $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
+
+ $resource->openTransaction();
+ $resource
+ ->setStatus(DrydockResourceStatus::STATUS_CLOSED)
+ ->save();
+
+ // TODO: Hold slot locks until destruction?
+ DrydockSlotLock::releaseLocks($resource->getPHID());
+ $resource->saveTransaction();
+
+ $statuses = array(
+ DrydockLeaseStatus::STATUS_PENDING,
+ DrydockLeaseStatus::STATUS_ACQUIRED,
+ DrydockLeaseStatus::STATUS_ACTIVE,
+ );
+
+ $leases = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
+ ->withResourceIDs(array($resource->getID()))
+ ->withStatuses($statuses)
+ ->execute();
+
+ foreach ($leases as $lease) {
+ $command = DrydockCommand::initializeNewCommand($viewer)
+ ->setTargetPHID($lease->getPHID())
+ ->setAuthorPHID($drydock_phid)
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
+
+ $lease->scheduleUpdate();
+ }
+
+ // TODO: Schedule resource destruction.
+ }
+
+}
diff --git a/src/applications/drydock/worker/DrydockWorker.php b/src/applications/drydock/worker/DrydockWorker.php
--- a/src/applications/drydock/worker/DrydockWorker.php
+++ b/src/applications/drydock/worker/DrydockWorker.php
@@ -36,4 +36,18 @@
return $resource;
}
+ protected function loadCommands($target_phid) {
+ $viewer = $this->getViewer();
+
+ $commands = id(new DrydockCommandQuery())
+ ->setViewer($viewer)
+ ->withTargetPHIDs(array($target_phid))
+ ->withConsumed(false)
+ ->execute();
+
+ $commands = msort($commands, 'getID');
+
+ return $commands;
+ }
+
}
diff --git a/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php b/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php
--- a/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php
+++ b/src/applications/harbormaster/artifact/HarbormasterHostArtifact.php
@@ -62,13 +62,16 @@
public function releaseArtifact(PhabricatorUser $actor) {
$lease = $this->loadArtifactLease($actor);
- $resource = $lease->getResource();
- $blueprint = $resource->getBlueprint();
-
- if ($lease->isActive()) {
- $blueprint->releaseLease($resource, $lease);
+ if (!$lease->canRelease()) {
+ return;
}
- }
+ $command = DrydockCommand::initializeNewCommand($actor)
+ ->setTargetPHID($lease->getPHID())
+ ->setCommand(DrydockCommand::COMMAND_RELEASE)
+ ->save();
+
+ $lease->scheduleUpdate();
+ }
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 28, 11:31 AM (6 h, 40 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6937917
Default Alt Text
D14143.diff (47 KB)
Attached To
Mode
D14143: Add a command queue to Drydock to manage lease/resource release
Attached
Detach File
Event Timeline
Log In to Comment