Page MenuHomePhabricator

D14143.diff
No OneTemporary

D14143.diff

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

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)

Event Timeline