Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
index bc8c7e8539..5b794d3479 100644
--- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
@@ -1,454 +1,455 @@
<?php
/**
* @task lease Lease Acquisition
* @task resource Resource Allocation
* @task log Logging
*/
abstract class DrydockBlueprintImplementation {
private $activeResource;
private $activeLease;
private $instance;
abstract public function getType();
abstract public function getInterface(
DrydockResource $resource,
DrydockLease $lease,
$type);
abstract public function isEnabled();
abstract public function getDescription();
public function getBlueprintClass() {
return get_class($this);
}
protected function loadLease($lease_id) {
+ // TODO: Get rid of this?
$query = id(new DrydockLeaseQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
->withIDs(array($lease_id))
- ->needResources(true)
->execute();
$lease = idx($query, $lease_id);
if (!$lease) {
throw new Exception("No such lease '{$lease_id}'!");
}
return $lease;
}
protected function getInstance() {
if (!$this->instance) {
throw new Exception(
"Attach the blueprint instance to the implementation.");
}
return $this->instance;
}
public function attachInstance(DrydockBlueprint $instance) {
$this->instance = $instance;
return $this;
}
/* -( Lease Acquisition )-------------------------------------------------- */
/**
* @task lease
*/
final public function filterResource(
DrydockResource $resource,
DrydockLease $lease) {
$scope = $this->pushActiveScope($resource, $lease);
return $this->canAllocateLease($resource, $lease);
}
/**
* Enforce basic checks on lease/resource compatibility. Allows resources to
* reject leases if they are incompatible, even if the resource types match.
*
* For example, if a resource represents a 32-bit host, this method might
* reject leases that need a 64-bit host. If a resource represents a working
* copy of repository "X", this method might reject leases which need a
* working copy of repository "Y". Generally, although the main types of
* a lease and resource may match (e.g., both "host"), it may not actually be
* possible to satisfy the lease with a specific resource.
*
* This method generally should not enforce limits or perform capacity
* checks. Perform those in @{method:shouldAllocateLease} instead. It also
* should not perform actual acquisition of the lease; perform that in
* @{method:executeAcquireLease} instead.
*
* @param DrydockResource Candidiate resource to allocate the lease on.
* @param DrydockLease Pending lease that wants to allocate here.
* @return bool True if the resource and lease are compatible.
* @task lease
*/
abstract protected function canAllocateLease(
DrydockResource $resource,
DrydockLease $lease);
/**
* @task lease
*/
final public function allocateLease(
DrydockResource $resource,
DrydockLease $lease) {
$scope = $this->pushActiveScope($resource, $lease);
$this->log('Trying to Allocate Lease');
$lease->setStatus(DrydockLeaseStatus::STATUS_ACQUIRING);
$lease->setResourceID($resource->getID());
$lease->attachResource($resource);
$ephemeral_lease = id(clone $lease)->makeEphemeral();
$allocated = false;
$allocation_exception = null;
$resource->openTransaction();
$resource->beginReadLocking();
$resource->reload();
$other_leases = id(new DrydockLease())->loadAllWhere(
'status IN (%Ld) AND resourceID = %d',
array(
DrydockLeaseStatus::STATUS_ACQUIRING,
DrydockLeaseStatus::STATUS_ACTIVE,
),
$resource->getID());
try {
$allocated = $this->shouldAllocateLease(
$resource,
$ephemeral_lease,
$other_leases);
} catch (Exception $ex) {
$allocation_exception = $ex;
}
if ($allocated) {
$lease->save();
}
$resource->endReadLocking();
if ($allocated) {
$resource->saveTransaction();
$this->log('Allocated Lease');
} else {
$resource->killTransaction();
$this->log('Failed to Allocate Lease');
}
if ($allocation_exception) {
$this->logException($allocation_exception);
}
return $allocated;
}
/**
* Enforce lease limits on resources. Allows resources to reject leases if
* they would become over-allocated by accepting them.
*
* For example, if a resource represents disk space, this method might check
* how much space the lease is asking for (say, 200MB) and how much space is
* left unallocated on the resource. It could grant the lease (return true)
* if it has enough remaining space (more than 200MB), and reject the lease
* (return false) if it does not (less than 200MB).
*
* A resource might also allow only exclusive leases. In this case it could
* accept a new lease (return true) if there are no active leases, or reject
* the new lease (return false) if there any other leases.
*
* A lock is held on the resource while this method executes to prevent
* multiple processes from allocating leases on the resource simultaneously.
* However, this means you should implement the method as cheaply as possible.
* In particular, do not perform any actual acquisition or setup in this
* method.
*
* If allocation is permitted, the lease will be moved to `ACQUIRING` status
* and @{method:executeAcquireLease} will be called to actually perform
* acquisition.
*
* General compatibility checks unrelated to resource limits and capacity are
* better implemented in @{method:canAllocateLease}, which serves as a
* cheap filter before lock acquisition.
*
* @param DrydockResource Candidate resource to allocate the lease on.
* @param DrydockLease Pending lease that wants to allocate here.
* @param list<DrydockLease> Other allocated and acquired leases on the
* resource. The implementation can inspect them
* to verify it can safely add the new lease.
* @return bool True to allocate the lease on the resource;
* false to reject it.
* @task lease
*/
abstract protected function shouldAllocateLease(
DrydockResource $resource,
DrydockLease $lease,
array $other_leases);
/**
* @task lease
*/
final public function acquireLease(
DrydockResource $resource,
DrydockLease $lease) {
$scope = $this->pushActiveScope($resource, $lease);
$this->log('Acquiring Lease');
$lease->setStatus(DrydockLeaseStatus::STATUS_ACTIVE);
$lease->setResourceID($resource->getID());
$lease->attachResource($resource);
$ephemeral_lease = id(clone $lease)->makeEphemeral();
try {
$this->executeAcquireLease($resource, $ephemeral_lease);
} catch (Exception $ex) {
$this->logException($ex);
throw $ex;
}
$lease->setAttributes($ephemeral_lease->getAttributes());
$lease->save();
$this->log('Acquired Lease');
}
/**
* Acquire and activate an allocated lease. Allows resources to peform setup
* as leases are brought online.
*
* Following a successful call to @{method:canAllocateLease}, a lease is moved
* to `ACQUIRING` status and this method is called after resource locks are
* released. Nothing is locked while this method executes; the implementation
* is free to perform expensive operations like writing files and directories,
* executing commands, etc.
*
* After this method executes, the lease status is moved to `ACTIVE` and the
* original leasee may access it.
*
* If acquisition fails, throw an exception.
*
* @param DrydockResource Resource to acquire a lease on.
* @param DrydockLease Lease to acquire.
* @return void
*/
abstract protected function executeAcquireLease(
DrydockResource $resource,
DrydockLease $lease);
final public function releaseLease(
DrydockResource $resource,
DrydockLease $lease) {
$scope = $this->pushActiveScope(null, $lease);
$released = false;
$lease->openTransaction();
$lease->beginReadLocking();
$lease->reload();
if ($lease->getStatus() == DrydockLeaseStatus::STATUS_ACTIVE) {
$lease->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
$lease->save();
$released = true;
}
$lease->endReadLocking();
$lease->saveTransaction();
if (!$released) {
throw new Exception("Unable to release lease: lease not active!");
}
}
/* -( Resource Allocation )------------------------------------------------ */
public function canAllocateMoreResources(array $pool) {
return true;
}
abstract protected function executeAllocateResource(DrydockLease $lease);
final public function allocateResource(DrydockLease $lease) {
$scope = $this->pushActiveScope(null, $lease);
$this->log(
pht(
"Blueprint '%s': Allocating Resource for '%s'",
$this->getBlueprintClass(),
$lease->getLeaseName()));
try {
$resource = $this->executeAllocateResource($lease);
$this->validateAllocatedResource($resource);
} catch (Exception $ex) {
$this->logException($ex);
throw $ex;
}
return $resource;
}
/* -( Logging )------------------------------------------------------------ */
/**
* @task log
*/
protected function logException(Exception $ex) {
$this->log($ex->getMessage());
}
/**
* @task log
*/
protected function log($message) {
self::writeLog(
$this->activeResource,
$this->activeLease,
$message);
}
/**
* @task log
*/
public static function writeLog(
DrydockResource $resource = null,
DrydockLease $lease = null,
$message) {
$log = id(new DrydockLog())
->setEpoch(time())
->setMessage($message);
if ($resource) {
$log->setResourceID($resource->getID());
}
if ($lease) {
$log->setLeaseID($lease->getID());
}
$log->save();
}
public static function getAllBlueprintImplementations() {
static $list = null;
if ($list === null) {
$blueprints = id(new PhutilSymbolLoader())
->setType('class')
->setAncestorClass('DrydockBlueprintImplementation')
->setConcreteOnly(true)
->selectAndLoadSymbols();
$list = ipull($blueprints, 'name', 'name');
foreach ($list as $class_name => $ignored) {
$list[$class_name] = newv($class_name, array());
}
}
return $list;
}
public static function getAllBlueprintImplementationsForResource($type) {
static $groups = null;
if ($groups === null) {
$groups = mgroup(self::getAllBlueprintImplementations(), 'getType');
}
return idx($groups, $type, array());
}
protected function newResourceTemplate($name) {
$resource = new DrydockResource();
$resource->setBlueprintPHID($this->getInstance()->getPHID());
$resource->setBlueprintClass($this->getBlueprintClass());
$resource->setType($this->getType());
$resource->setStatus(DrydockResourceStatus::STATUS_PENDING);
$resource->setName($name);
$resource->save();
$this->activeResource = $resource;
$this->log(
pht(
"Blueprint '%s': Created New Template",
$this->getBlueprintClass()));
return $resource;
}
/**
* Sanity checks that the blueprint is implemented properly.
*/
private function validateAllocatedResource($resource) {
$blueprint = $this->getBlueprintClass();
if (!($resource instanceof DrydockResource)) {
throw new Exception(
"Blueprint '{$blueprint}' is not properly implemented: ".
"executeAllocateResource() must return an object of type ".
"DrydockResource or throw, but returned something else.");
}
$current_status = $resource->getStatus();
$req_status = DrydockResourceStatus::STATUS_OPEN;
if ($current_status != $req_status) {
$current_name = DrydockResourceStatus::getNameForStatus($current_status);
$req_name = DrydockResourceStatus::getNameForStatus($req_status);
throw new Exception(
"Blueprint '{$blueprint}' is not properly implemented: ".
"executeAllocateResource() must return a DrydockResource with ".
"status '{$req_name}', but returned one with status ".
"'{$current_name}'.");
}
}
private function pushActiveScope(
DrydockResource $resource = null,
DrydockLease $lease = null) {
if (($this->activeResource !== null) ||
($this->activeLease !== null)) {
throw new Exception("There is already an active resource or lease!");
}
$this->activeResource = $resource;
$this->activeLease = $lease;
return new DrydockBlueprintScopeGuard($this);
}
public function popActiveScope() {
$this->activeResource = null;
$this->activeLease = null;
}
}
diff --git a/src/applications/drydock/controller/DrydockLeaseListController.php b/src/applications/drydock/controller/DrydockLeaseListController.php
index 791299ff4f..b168f9bc7c 100644
--- a/src/applications/drydock/controller/DrydockLeaseListController.php
+++ b/src/applications/drydock/controller/DrydockLeaseListController.php
@@ -1,46 +1,46 @@
<?php
final class DrydockLeaseListController extends DrydockController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$nav = $this->buildSideNav('lease');
$pager = new AphrontPagerView();
$pager->setURI(new PhutilURI('/drydock/lease/'), 'offset');
$pager->setOffset($request->getInt('offset'));
$leases = id(new DrydockLeaseQuery())
- ->needResources(true)
+ ->setViewer($user)
->executeWithOffsetPager($pager);
$title = pht('Leases');
$header = id(new PHUIHeaderView())
->setHeader($title);
$lease_list = $this->buildLeaseListView($leases);
$nav->appendChild(
array(
$header,
$lease_list,
$pager,
));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, $request->getRequestURI());
$nav->setCrumbs($crumbs);
return $this->buildApplicationPage(
$nav,
array(
'device' => true,
'title' => $title,
));
}
}
diff --git a/src/applications/drydock/controller/DrydockLeaseReleaseController.php b/src/applications/drydock/controller/DrydockLeaseReleaseController.php
index dd238e2467..f3cc501569 100644
--- a/src/applications/drydock/controller/DrydockLeaseReleaseController.php
+++ b/src/applications/drydock/controller/DrydockLeaseReleaseController.php
@@ -1,55 +1,58 @@
<?php
final class DrydockLeaseReleaseController extends DrydockController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
- $lease = id(new DrydockLease())->load($this->id);
+ $lease = id(new DrydockLeaseQuery())
+ ->setViewer($user)
+ ->withIDs(array($this->id))
+ ->executeOne();
if (!$lease) {
return new Aphront404Response();
}
$lease_uri = '/lease/'.$lease->getID().'/';
$lease_uri = $this->getApplicationURI($lease_uri);
if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
$dialog = id(new AphrontDialogView())
->setUser($user)
->setTitle(pht('Lease Not Active'))
->appendChild(phutil_tag('p', array(), pht(
'You can only release "active" leases.')))
->addCancelButton($lease_uri);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
if (!$request->isDialogFormPost()) {
$dialog = id(new AphrontDialogView())
->setUser($user)
->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);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
- $resource = $lease->loadResource();
+ $resource = $lease->getResource();
$blueprint = $resource->getBlueprint();
$blueprint->releaseLease($resource, $lease);
return id(new AphrontReloadResponse())->setURI($lease_uri);
}
}
diff --git a/src/applications/drydock/controller/DrydockResourceViewController.php b/src/applications/drydock/controller/DrydockResourceViewController.php
index 46ecc1a4c4..459d0160bf 100644
--- a/src/applications/drydock/controller/DrydockResourceViewController.php
+++ b/src/applications/drydock/controller/DrydockResourceViewController.php
@@ -1,127 +1,127 @@
<?php
final class DrydockResourceViewController extends DrydockController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$resource = id(new DrydockResource())->load($this->id);
if (!$resource) {
return new Aphront404Response();
}
$title = 'Resource '.$resource->getID().' '.$resource->getName();
$header = id(new PHUIHeaderView())
->setHeader($title);
$actions = $this->buildActionListView($resource);
$properties = $this->buildPropertyListView($resource, $actions);
$resource_uri = 'resource/'.$resource->getID().'/';
$resource_uri = $this->getApplicationURI($resource_uri);
$leases = id(new DrydockLeaseQuery())
+ ->setViewer($user)
->withResourceIDs(array($resource->getID()))
- ->needResources(true)
->execute();
$lease_list = $this->buildLeaseListView($leases);
$lease_list->setNoDataString(pht('This resource has no leases.'));
$pager = new AphrontPagerView();
$pager->setURI(new PhutilURI($resource_uri), 'offset');
$pager->setOffset($request->getInt('offset'));
$logs = id(new DrydockLogQuery())
->withResourceIDs(array($resource->getID()))
->executeWithOffsetPager($pager);
$log_table = $this->buildLogTableView($logs);
$log_table->appendChild($pager);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setActionList($actions);
$crumbs->addTextCrumb(pht('Resource %d', $resource->getID()));
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$lease_list,
$log_table,
),
array(
'device' => true,
'title' => $title,
));
}
private function buildActionListView(DrydockResource $resource) {
$view = id(new PhabricatorActionListView())
->setUser($this->getRequest()->getUser())
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($resource);
$can_close = ($resource->getStatus() == DrydockResourceStatus::STATUS_OPEN);
$uri = '/resource/'.$resource->getID().'/close/';
$uri = $this->getApplicationURI($uri);
$view->addAction(
id(new PhabricatorActionView())
->setHref($uri)
->setName(pht('Close Resource'))
->setIcon('delete')
->setWorkflow(true)
->setDisabled(!$can_close));
return $view;
}
private function buildPropertyListView(
DrydockResource $resource,
PhabricatorActionListView $actions) {
$view = new PHUIPropertyListView();
$view->setActionList($actions);
$status = $resource->getStatus();
$status = DrydockResourceStatus::getNameForStatus($status);
$view->addProperty(
pht('Status'),
$status);
$view->addProperty(
pht('Resource Type'),
$resource->getType());
// TODO: Load handle.
$view->addProperty(
pht('Blueprint'),
$resource->getBlueprintPHID());
$attributes = $resource->getAttributes();
if ($attributes) {
$view->addSectionHeader(pht('Attributes'));
foreach ($attributes as $key => $value) {
$view->addProperty($key, $value);
}
}
return $view;
}
}
diff --git a/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php b/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
index 54b98bf62d..5f482786ad 100644
--- a/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
+++ b/src/applications/drydock/management/DrydockManagementReleaseWorkflow.php
@@ -1,45 +1,52 @@
<?php
final class DrydockManagementReleaseWorkflow
extends DrydockManagementWorkflow {
public function didConstruct() {
$this
->setName('release')
->setSynopsis('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(
"Specify one or more lease IDs to release.");
}
+ $viewer = PhabricatorUser::getOmnipotentUser();
+
+ $leases = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
+ ->withIDs($ids)
+ ->execute();
+
foreach ($ids as $id) {
- $lease = id(new DrydockLease())->load($id);
+ $lease = idx($leases, $id);
if (!$lease) {
$console->writeErr("Lease %d does not exist!\n", $id);
} else if ($lease->getStatus() != DrydockLeaseStatus::STATUS_ACTIVE) {
$console->writeErr("Lease %d is not 'active'!\n", $id);
} else {
- $resource = $lease->loadResource();
+ $resource = $lease->getResource();
$blueprint = $resource->getBlueprint();
$blueprint->releaseLease($resource, $lease);
$console->writeErr("Released lease %d.\n", $id);
}
}
}
}
diff --git a/src/applications/drydock/query/DrydockLeaseQuery.php b/src/applications/drydock/query/DrydockLeaseQuery.php
index 9b67acd363..05cd40c49a 100644
--- a/src/applications/drydock/query/DrydockLeaseQuery.php
+++ b/src/applications/drydock/query/DrydockLeaseQuery.php
@@ -1,84 +1,79 @@
<?php
-final class DrydockLeaseQuery extends PhabricatorOffsetPagedQuery {
+final class DrydockLeaseQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $resourceIDs;
- private $needResources;
-
- public function withResourceIDs(array $ids) {
- $this->resourceIDs = $ids;
- return $this;
- }
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
- public function needResources($need_resources) {
- $this->needResources = $need_resources;
+ public function withResourceIDs(array $ids) {
+ $this->resourceIDs = $ids;
return $this;
}
- public function execute() {
+ public function loadPage() {
$table = new DrydockLease();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT lease.* FROM %T lease %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
- $leases = $table->loadAllFromArray($data);
+ return $table->loadAllFromArray($data);
+ }
- if ($leases && $this->needResources) {
- $resources = id(new DrydockResource())->loadAllWhere(
- 'id IN (%Ld)',
- mpull($leases, 'getResourceID'));
-
- foreach ($leases as $key => $lease) {
- if ($lease->getResourceID()) {
- $resource = idx($resources, $lease->getResourceID());
- if ($resource) {
- $lease->attachResource($resource);
- } else {
- unset($leases[$key]);
- }
- } else {
- unset($leases[$key]);
- }
+ public function willFilterPage(array $leases) {
+ $resources = id(new DrydockResourceQuery())
+ ->setParentQuery($this)
+ ->setViewer($this->getViewer())
+ ->withIDs(mpull($leases, 'getResourceID'))
+ ->execute();
+
+ foreach ($leases as $key => $lease) {
+ $resource = idx($resources, $lease->getResourceID());
+ if (!$resource) {
+ unset($leases[$key]);
+ continue;
}
+ $lease->attachResource($resource);
}
return $leases;
}
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->resourceIDs) {
$where[] = qsprintf(
$conn_r,
'resourceID IN (%Ld)',
$this->resourceIDs);
}
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
+ $where[] = $this->buildPagingClause($conn_r);
+
return $this->formatWhereClause($where);
}
- private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
- return qsprintf($conn_r, 'ORDER BY id DESC');
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationDrydock';
}
}
diff --git a/src/applications/drydock/storage/DrydockLease.php b/src/applications/drydock/storage/DrydockLease.php
index 9e08dbd3b1..fdaac35e9f 100644
--- a/src/applications/drydock/storage/DrydockLease.php
+++ b/src/applications/drydock/storage/DrydockLease.php
@@ -1,190 +1,213 @@
<?php
-final class DrydockLease extends DrydockDAO {
+final class DrydockLease extends DrydockDAO
+ implements PhabricatorPolicyInterface {
protected $resourceID;
protected $resourceType;
protected $until;
protected $ownerPHID;
protected $attributes = array();
protected $status = DrydockLeaseStatus::STATUS_PENDING;
protected $taskID;
private $resource = self::ATTACHABLE;
private $releaseOnDestruction;
/**
* Flag this lease to be released when its destructor is called. This is
* mostly useful if you have a script which acquires, uses, and then releases
* a lease, as you don't need to explicitly handle exceptions to properly
* release the lease.
*/
public function releaseOnDestruction() {
$this->releaseOnDestruction = true;
return $this;
}
public function __destruct() {
if ($this->releaseOnDestruction) {
if ($this->isActive()) {
$this->release();
}
}
}
public function getLeaseName() {
return pht('Lease %d', $this->getID());
}
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'attributes' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function setAttribute($key, $value) {
$this->attributes[$key] = $value;
return $this;
}
public function getAttribute($key, $default = null) {
return idx($this->attributes, $key, $default);
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_DRYL);
}
public function getInterface($type) {
return $this->getResource()->getInterface($this, $type);
}
public function getResource() {
return $this->assertAttached($this->resource);
}
public function attachResource(DrydockResource $resource) {
$this->resource = $resource;
return $this;
}
public function hasAttachedResource() {
return ($this->resource !== null);
}
public function loadResource() {
return id(new DrydockResource())->loadOneWhere(
'id = %d',
$this->getResourceID());
}
public function queueForActivation() {
if ($this->getID()) {
throw new Exception(
"Only new leases may be queued for activation!");
}
$this->setStatus(DrydockLeaseStatus::STATUS_PENDING);
$this->save();
// NOTE: Prevent a race where some eager worker quickly grabs the task
// before we can save the Task ID.
$this->openTransaction();
$this->beginReadLocking();
$this->reload();
$task = PhabricatorWorker::scheduleTask(
'DrydockAllocatorWorker',
$this->getID());
$this->setTaskID($task->getID());
$this->save();
$this->endReadLocking();
$this->saveTransaction();
return $this;
}
public function release() {
$this->assertActive();
$this->setStatus(DrydockLeaseStatus::STATUS_RELEASED);
$this->save();
$this->resource = null;
return $this;
}
public function isActive() {
switch ($this->status) {
case DrydockLeaseStatus::STATUS_ACTIVE:
case DrydockLeaseStatus::STATUS_ACQUIRING:
return true;
}
return false;
}
private function assertActive() {
if (!$this->isActive()) {
throw new Exception(
"Lease is not active! You can not interact with resources through ".
"an inactive lease.");
}
}
public static function waitForLeases(array $leases) {
assert_instances_of($leases, 'DrydockLease');
$task_ids = array_filter(mpull($leases, 'getTaskID'));
PhabricatorWorker::waitForTasks($task_ids);
$unresolved = $leases;
while (true) {
foreach ($unresolved as $key => $lease) {
$lease->reload();
switch ($lease->getStatus()) {
case DrydockLeaseStatus::STATUS_ACTIVE:
unset($unresolved[$key]);
break;
case DrydockLeaseStatus::STATUS_RELEASED:
throw new Exception("Lease has already been released!");
case DrydockLeaseStatus::STATUS_EXPIRED:
throw new Exception("Lease has already expired!");
case DrydockLeaseStatus::STATUS_BROKEN:
throw new Exception("Lease has been broken!");
case DrydockLeaseStatus::STATUS_PENDING:
case DrydockLeaseStatus::STATUS_ACQUIRING:
break;
}
}
if ($unresolved) {
sleep(1);
} else {
break;
}
}
foreach ($leases as $lease) {
$lease->attachResource($lease->loadResource());
}
}
public function waitUntilActive() {
if (!$this->getID()) {
$this->queueForActivation();
}
self::waitForLeases(array($this));
return $this;
}
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ );
+ }
+
+ public function getPolicy($capability) {
+ return $this->getResource()->getPolicy($capability);
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return $this->getResource()->hasAutomaticCapability($capability, $viewer);
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return pht('Leases inherit policies from the resources they lease.');
+ }
+
}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php b/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
index 56bf746b5d..0511c526a9 100644
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildArtifact.php
@@ -1,145 +1,146 @@
<?php
final class HarbormasterBuildArtifact extends HarbormasterDAO
implements PhabricatorPolicyInterface {
protected $buildTargetPHID;
protected $artifactType;
protected $artifactIndex;
protected $artifactKey;
protected $artifactData = array();
private $buildTarget = self::ATTACHABLE;
const TYPE_FILE = 'file';
const TYPE_HOST = 'host';
public static function initializeNewBuildArtifact(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildArtifact())
->setBuildTargetPHID($build_target->getPHID());
}
public function getConfiguration() {
return array(
self::CONFIG_SERIALIZATION => array(
'artifactData' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function attachBuildTarget(HarbormasterBuildTarget $build_target) {
$this->buildTarget = $build_target;
return $this;
}
public function getBuildTarget() {
return $this->assertAttached($this->buildTarget);
}
public function setArtifactKey($build_phid, $key) {
$this->artifactIndex =
PhabricatorHash::digestForIndex($build_phid.$key);
$this->artifactKey = $key;
return $this;
}
public function getObjectItemView(PhabricatorUser $viewer) {
$data = $this->getArtifactData();
switch ($this->getArtifactType()) {
case self::TYPE_FILE:
$handle = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($data)
->executeOne();
return id(new PHUIObjectItemView())
->setObjectName(pht('File'))
->setHeader($handle->getFullName())
->setHref($handle->getURI());
case self::TYPE_HOST:
$leases = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
->withIDs(array($data["drydock-lease"]))
->execute();
$lease = $leases[$data["drydock-lease"]];
return id(new PHUIObjectItemView())
->setObjectName(pht('Drydock Lease'))
->setHeader($lease->getID())
->setHref('/drydock/lease/'.$lease->getID());
default:
return null;
}
}
public function loadDrydockLease() {
if ($this->getArtifactType() !== self::TYPE_HOST) {
throw new Exception(
"`loadDrydockLease` may only be called on host artifacts.");
}
$data = $this->getArtifactData();
// FIXME: Is there a better way of doing this?
$lease = id(new DrydockLease())->load(
$data['drydock-lease']);
if ($lease === null) {
throw new Exception("Associated Drydock lease not found!");
}
$resource = id(new DrydockResource())->load(
$lease->getResourceID());
if ($resource === null) {
throw new Exception("Associated Drydock resource not found!");
}
$lease->attachResource($resource);
return $lease;
}
public function loadPhabricatorFile() {
if ($this->getArtifactType() !== self::TYPE_FILE) {
throw new Exception(
"`loadPhabricatorFile` may only be called on file artifacts.");
}
$data = $this->getArtifactData();
// The data for TYPE_FILE is an array with a single PHID in it.
$phid = $data["filePHID"];
$file = id(new PhabricatorFileQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($phid))
->executeOne();
if ($file === null) {
throw new Exception("Associated file not found!");
}
return $file;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
return $this->getBuildTarget()->getPolicy($capability);
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getBuildTarget()->hasAutomaticCapability(
$capability,
$viewer);
}
public function describeAutomaticCapability($capability) {
return pht(
'Users must be able to see a buildable to see its artifacts.');
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 24, 11:28 AM (1 d, 10 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
182711
Default Alt Text
(36 KB)

Event Timeline