Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/autopatches/20150930.drydock.log.1.sql b/resources/sql/autopatches/20150930.drydock.log.1.sql
new file mode 100644
index 0000000000..e84859b718
--- /dev/null
+++ b/resources/sql/autopatches/20150930.drydock.log.1.sql
@@ -0,0 +1,25 @@
+TRUNCATE {$NAMESPACE}_drydock.drydock_log;
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ DROP resourceID;
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ DROP leaseID;
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ DROP message;
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ ADD blueprintPHID VARBINARY(64);
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ ADD resourcePHID VARBINARY(64);
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ ADD leasePHID VARBINARY(64);
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ ADD type VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT};
+
+ALTER TABLE {$NAMESPACE}_drydock.drydock_log
+ ADD data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT};
diff --git a/src/applications/drydock/application/PhabricatorDrydockApplication.php b/src/applications/drydock/application/PhabricatorDrydockApplication.php
index 5df54593ee..e662fea9e6 100644
--- a/src/applications/drydock/application/PhabricatorDrydockApplication.php
+++ b/src/applications/drydock/application/PhabricatorDrydockApplication.php
@@ -1,102 +1,105 @@
<?php
final class PhabricatorDrydockApplication extends PhabricatorApplication {
public function getBaseURI() {
return '/drydock/';
}
public function getName() {
return pht('Drydock');
}
public function getShortDescription() {
return pht('Allocate Software Resources');
}
public function getFontIcon() {
return 'fa-truck';
}
public function getTitleGlyph() {
return "\xE2\x98\x82";
}
public function getFlavorText() {
return pht('A nautical adventure.');
}
public function getApplicationGroup() {
return self::GROUP_UTILITIES;
}
public function isPrototype() {
return true;
}
public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
return array(
array(
'name' => pht('Drydock User Guide'),
'href' => PhabricatorEnv::getDoclink('Drydock User Guide'),
),
);
}
public function getRoutes() {
return array(
'/drydock/' => array(
'' => 'DrydockConsoleController',
- 'blueprint/' => array(
+ '(?P<type>blueprint)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockBlueprintListController',
'(?P<id>[1-9]\d*)/' => array(
'' => 'DrydockBlueprintViewController',
'(?P<action>disable|enable)/' =>
'DrydockBlueprintDisableController',
'resources/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockResourceListController',
+ 'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
+ 'DrydockLogListController',
),
'create/' => 'DrydockBlueprintCreateController',
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
),
- 'resource/' => array(
+ '(?P<type>resource)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController',
'(?P<id>[1-9]\d*)/' => array(
'' => 'DrydockResourceViewController',
'release/' => 'DrydockResourceReleaseController',
'leases/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockLeaseListController',
+ 'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
+ 'DrydockLogListController',
),
),
- 'lease/' => array(
+ '(?P<type>lease)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLeaseListController',
'(?P<id>[1-9]\d*)/' => array(
'' => 'DrydockLeaseViewController',
'release/' => 'DrydockLeaseReleaseController',
+ 'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
+ 'DrydockLogListController',
),
),
- 'log/' => array(
- '(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLogListController',
- ),
),
);
}
protected function getCustomCapabilities() {
return array(
DrydockDefaultViewCapability::CAPABILITY => array(
'template' => DrydockBlueprintPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
),
DrydockDefaultEditCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
'template' => DrydockBlueprintPHIDType::TYPECONST,
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
),
DrydockCreateBlueprintsCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
);
}
}
diff --git a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
index f58767c4fc..b7e39a49a4 100644
--- a/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
+++ b/src/applications/drydock/blueprint/DrydockBlueprintImplementation.php
@@ -1,339 +1,299 @@
<?php
/**
* @task lease Lease Acquisition
* @task resource Resource Allocation
* @task interface Resource Interfaces
* @task log Logging
*/
abstract class DrydockBlueprintImplementation extends Phobject {
abstract public function getType();
abstract public function isEnabled();
abstract public function getBlueprintName();
abstract public function getDescription();
public function getFieldSpecifications() {
return array();
}
/* -( Lease Acquisition )-------------------------------------------------- */
/**
* 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. The blueprint might also reject
* a resource if the lease needs 8GB of RAM and the resource only has 6GB
* free.
*
* This method should not acquire locks or expect anything to be locked. This
* is a coarse compatibility check between a lease and a resource.
*
* @param DrydockBlueprint Concrete blueprint to allocate for.
* @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 public function canAcquireLeaseOnResource(
DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease);
/**
* Acquire a lease. Allows resources to peform setup as leases are brought
* online.
*
* If acquisition fails, throw an exception.
*
* @param DrydockBlueprint Blueprint which built the resource.
* @param DrydockResource Resource to acquire a lease on.
* @param DrydockLease Requested lease.
* @return void
* @task lease
*/
abstract public function acquireLease(
DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease);
/**
* @return void
* @task lease
*/
public function activateLease(
DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease) {
throw new PhutilMethodNotImplementedException();
}
/**
* React to a lease being released.
*
* This callback is primarily useful for automatically releasing resources
* once all leases are released.
*
* @param DrydockBlueprint Blueprint which built the resource.
* @param DrydockResource Resource a lease was released on.
* @param DrydockLease Recently released lease.
* @return void
* @task lease
*/
abstract public function didReleaseLease(
DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease);
/**
* Destroy any temporary data associated with a lease.
*
* If a lease creates temporary state while held, destroy it here.
*
* @param DrydockBlueprint Blueprint which built the resource.
* @param DrydockResource Resource the lease is acquired on.
* @param DrydockLease The lease being destroyed.
* @return void
* @task lease
*/
abstract public function destroyLease(
DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease);
/* -( Resource Allocation )------------------------------------------------ */
/**
* Enforce fundamental implementation/lease checks. Allows implementations to
* reject a lease which no concrete blueprint can ever satisfy.
*
* For example, if a lease only builds ARM hosts and the lease needs a
* PowerPC host, it may be rejected here.
*
* This is the earliest rejection phase, and followed by
* @{method:canEverAllocateResourceForLease}.
*
* This method should not actually check if a resource can be allocated
* right now, or even if a blueprint which can allocate a suitable resource
* really exists, only if some blueprint may conceivably exist which could
* plausibly be able to build a suitable resource.
*
* @param DrydockLease Requested lease.
* @return bool True if some concrete blueprint of this implementation's
* type might ever be able to build a resource for the lease.
* @task resource
*/
abstract public function canAnyBlueprintEverAllocateResourceForLease(
DrydockLease $lease);
/**
* Enforce basic blueprint/lease checks. Allows blueprints to reject a lease
* which they can not build a resource for.
*
* This is the second rejection phase. It follows
* @{method:canAnyBlueprintEverAllocateResourceForLease} and is followed by
* @{method:canAllocateResourceForLease}.
*
* This method should not check if a resource can be built right now, only
* if the blueprint as configured may, at some time, be able to build a
* suitable resource.
*
* @param DrydockBlueprint Blueprint which may be asked to allocate a
* resource.
* @param DrydockLease Requested lease.
* @return bool True if this blueprint can eventually build a suitable
* resource for the lease, as currently configured.
* @task resource
*/
abstract public function canEverAllocateResourceForLease(
DrydockBlueprint $blueprint,
DrydockLease $lease);
/**
* Enforce basic availability limits. Allows blueprints to reject resource
* allocation if they are currently overallocated.
*
* This method should perform basic capacity/limit checks. For example, if
* it has a limit of 6 resources and currently has 6 resources allocated,
* it might reject new leases.
*
* This method should not acquire locks or expect locks to be acquired. This
* is a coarse check to determine if the operation is likely to succeed
* right now without needing to acquire locks.
*
* It is expected that this method will sometimes return `true` (indicating
* that a resource can be allocated) but find that another allocator has
* eaten up free capacity by the time it actually tries to build a resource.
* This is normal and the allocator will recover from it.
*
* @param DrydockBlueprint The blueprint which may be asked to allocate a
* resource.
* @param DrydockLease Requested lease.
* @return bool True if this blueprint appears likely to be able to allocate
* a suitable resource.
* @task resource
*/
abstract public function canAllocateResourceForLease(
DrydockBlueprint $blueprint,
DrydockLease $lease);
/**
* Allocate a suitable resource for a lease.
*
* This method MUST acquire, hold, and manage locks to prevent multiple
* allocations from racing. World state is not locked before this method is
* called. Blueprints are entirely responsible for any lock handling they
* need to perform.
*
* @param DrydockBlueprint The blueprint which should allocate a resource.
* @param DrydockLease Requested lease.
* @return DrydockResource Allocated resource.
* @task resource
*/
abstract public function allocateResource(
DrydockBlueprint $blueprint,
DrydockLease $lease);
/**
* @task resource
*/
public function activateResource(
DrydockBlueprint $blueprint,
DrydockResource $resource) {
throw new PhutilMethodNotImplementedException();
}
/**
* Destroy any temporary data associated with a resource.
*
* If a resource creates temporary state when allocated, destroy that state
* here. For example, you might shut down a virtual host or destroy a working
* copy on disk.
*
* @param DrydockBlueprint Blueprint which built the resource.
* @param DrydockResource Resource being destroyed.
* @return void
* @task resource
*/
abstract public function destroyResource(
DrydockBlueprint $blueprint,
DrydockResource $resource);
/* -( Resource Interfaces )------------------------------------------------ */
abstract public function getInterface(
DrydockBlueprint $blueprint,
DrydockResource $resource,
DrydockLease $lease,
$type);
/* -( Logging )------------------------------------------------------------ */
- /**
- * @task log
- */
- protected function logException(Exception $ex) {
- $this->log($ex->getMessage());
- }
-
-
- /**
- * @task log
- */
- protected function log($message) {
- self::writeLog(null, null, $message);
- }
-
-
- /**
- * @task log
- */
- public static function writeLog(
- DrydockResource $resource = null,
- DrydockLease $lease = null,
- $message = null) {
-
- $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() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->execute();
}
public static function getNamedImplementation($class) {
return idx(self::getAllBlueprintImplementations(), $class);
}
protected function newResourceTemplate(
DrydockBlueprint $blueprint,
$name) {
$resource = id(new DrydockResource())
->setBlueprintPHID($blueprint->getPHID())
->attachBlueprint($blueprint)
->setType($this->getType())
->setStatus(DrydockResourceStatus::STATUS_PENDING)
->setName($name);
// Pre-allocate the resource PHID.
$resource->setPHID($resource->generatePHID());
return $resource;
}
protected function newLease(DrydockBlueprint $blueprint) {
return id(new DrydockLease());
}
protected function requireActiveLease(DrydockLease $lease) {
$lease_status = $lease->getStatus();
switch ($lease_status) {
case DrydockLeaseStatus::STATUS_ACQUIRED:
// TODO: Temporary failure.
throw new Exception(pht('Lease still activating.'));
case DrydockLeaseStatus::STATUS_ACTIVE:
return;
default:
// TODO: Permanent failure.
throw new Exception(pht('Lease in bad state.'));
}
}
}
diff --git a/src/applications/drydock/controller/DrydockBlueprintViewController.php b/src/applications/drydock/controller/DrydockBlueprintViewController.php
index 6991e18fa2..7102962600 100644
--- a/src/applications/drydock/controller/DrydockBlueprintViewController.php
+++ b/src/applications/drydock/controller/DrydockBlueprintViewController.php
@@ -1,170 +1,178 @@
<?php
final class DrydockBlueprintViewController extends DrydockBlueprintController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$blueprint = id(new DrydockBlueprintQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$blueprint) {
return new Aphront404Response();
}
$title = $blueprint->getBlueprintName();
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($blueprint);
if ($blueprint->getIsDisabled()) {
$header->setStatus('fa-ban', 'red', pht('Disabled'));
} else {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
}
$actions = $this->buildActionListView($blueprint);
$properties = $this->buildPropertyListView($blueprint, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Blueprint %d', $blueprint->getID()));
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$field_list = PhabricatorCustomField::getObjectFields(
$blueprint,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($blueprint);
$field_list->appendFieldsToPropertyList(
$blueprint,
$viewer,
$properties);
$resource_box = $this->buildResourceBox($blueprint);
$timeline = $this->buildTransactionTimeline(
$blueprint,
new DrydockBlueprintTransactionQuery());
$timeline->setShouldTerminate(true);
+ $log_query = id(new DrydockLogQuery())
+ ->withBlueprintPHIDs(array($blueprint->getPHID()));
+
+ $log_box = $this->buildLogBox(
+ $log_query,
+ $this->getApplicationURI("blueprint/{$id}/logs/query/all/"));
+
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$resource_box,
+ $log_box,
$timeline,
),
array(
'title' => $title,
));
}
private function buildActionListView(DrydockBlueprint $blueprint) {
$viewer = $this->getViewer();
$id = $blueprint->getID();
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($blueprint);
$edit_uri = $this->getApplicationURI("blueprint/edit/{$id}/");
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$blueprint,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setHref($edit_uri)
->setName(pht('Edit Blueprint'))
->setIcon('fa-pencil')
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
if (!$blueprint->getIsDisabled()) {
$disable_name = pht('Disable Blueprint');
$disable_icon = 'fa-ban';
$disable_uri = $this->getApplicationURI("blueprint/{$id}/disable/");
} else {
$disable_name = pht('Enable Blueprint');
$disable_icon = 'fa-check';
$disable_uri = $this->getApplicationURI("blueprint/{$id}/enable/");
}
$view->addAction(
id(new PhabricatorActionView())
->setHref($disable_uri)
->setName($disable_name)
->setIcon($disable_icon)
->setWorkflow(true)
->setDisabled(!$can_edit));
return $view;
}
private function buildPropertyListView(
DrydockBlueprint $blueprint,
PhabricatorActionListView $actions) {
$view = new PHUIPropertyListView();
$view->setActionList($actions);
$view->addProperty(
pht('Type'),
$blueprint->getImplementation()->getBlueprintName());
return $view;
}
private function buildResourceBox(DrydockBlueprint $blueprint) {
$viewer = $this->getViewer();
$resources = id(new DrydockResourceQuery())
->setViewer($viewer)
->withBlueprintPHIDs(array($blueprint->getPHID()))
->withStatuses(
array(
DrydockResourceStatus::STATUS_PENDING,
DrydockResourceStatus::STATUS_ACTIVE,
))
->setLimit(100)
->execute();
$resource_list = id(new DrydockResourceListView())
->setUser($viewer)
->setResources($resources)
->render()
->setNoDataString(pht('This blueprint has no active resources.'));
$id = $blueprint->getID();
$resources_uri = "blueprint/{$id}/resources/query/all/";
$resources_uri = $this->getApplicationURI($resources_uri);
$resource_header = id(new PHUIHeaderView())
->setHeader(pht('Active Resources'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($resources_uri)
->setIconFont('fa-search')
->setText(pht('View All Resources')));
return id(new PHUIObjectBoxView())
->setHeader($resource_header)
->setObjectList($resource_list);
}
}
diff --git a/src/applications/drydock/controller/DrydockController.php b/src/applications/drydock/controller/DrydockController.php
index e0130bdf56..760334cbdf 100644
--- a/src/applications/drydock/controller/DrydockController.php
+++ b/src/applications/drydock/controller/DrydockController.php
@@ -1,88 +1,115 @@
<?php
abstract class DrydockController extends PhabricatorController {
abstract public function buildSideNavView();
public function buildApplicationMenu() {
return $this->buildSideNavView()->getMenu();
}
protected function buildLocksTab($owner_phid) {
$locks = DrydockSlotLock::loadLocks($owner_phid);
$rows = array();
foreach ($locks as $lock) {
$rows[] = array(
$lock->getID(),
$lock->getLockKey(),
);
}
$table = id(new AphrontTableView($rows))
->setNoDataString(pht('No slot locks held.'))
->setHeaders(
array(
pht('ID'),
pht('Lock Key'),
))
->setColumnClasses(
array(
null,
'wide',
));
return id(new PHUIPropertyListView())
->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);
}
+ protected function buildLogBox(DrydockLogQuery $query, $all_uri) {
+ $viewer = $this->getViewer();
+
+ $logs = $query
+ ->setViewer($viewer)
+ ->setLimit(100)
+ ->execute();
+
+ $log_table = id(new DrydockLogListView())
+ ->setUser($viewer)
+ ->setLogs($logs)
+ ->render();
+
+ $log_header = id(new PHUIHeaderView())
+ ->setHeader(pht('Logs'))
+ ->addActionLink(
+ id(new PHUIButtonView())
+ ->setTag('a')
+ ->setHref($all_uri)
+ ->setIconFont('fa-search')
+ ->setText(pht('View All Logs')));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeader($log_header)
+ ->setTable($log_table);
+ }
+
}
diff --git a/src/applications/drydock/controller/DrydockLeaseViewController.php b/src/applications/drydock/controller/DrydockLeaseViewController.php
index af893ca49b..b9cf592313 100644
--- a/src/applications/drydock/controller/DrydockLeaseViewController.php
+++ b/src/applications/drydock/controller/DrydockLeaseViewController.php
@@ -1,158 +1,147 @@
<?php
final class DrydockLeaseViewController extends DrydockLeaseController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$lease = id(new DrydockLeaseQuery())
->setViewer($viewer)
->withIDs(array($id))
->needUnconsumedCommands(true)
->executeOne();
if (!$lease) {
return new Aphront404Response();
}
- $lease_uri = $this->getApplicationURI('lease/'.$lease->getID().'/');
+ $id = $lease->getID();
+ $lease_uri = $this->getApplicationURI("lease/{$id}/");
$title = pht('Lease %d', $lease->getID());
$header = id(new PHUIHeaderView())
->setHeader($title);
if ($lease->isReleasing()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Releasing'));
}
$actions = $this->buildActionListView($lease);
$properties = $this->buildPropertyListView($lease, $actions);
- $pager = new PHUIPagerView();
- $pager->setURI(new PhutilURI($lease_uri), 'offset');
- $pager->setOffset($request->getInt('offset'));
+ $log_query = id(new DrydockLogQuery())
+ ->withLeasePHIDs(array($lease->getPHID()));
- $logs = id(new DrydockLogQuery())
- ->setViewer($viewer)
- ->withLeaseIDs(array($lease->getID()))
- ->executeWithOffsetPager($pager);
-
- $log_table = id(new DrydockLogListView())
- ->setUser($viewer)
- ->setLogs($logs)
- ->render();
- $log_table->appendChild($pager);
+ $log_box = $this->buildLogBox(
+ $log_query,
+ $this->getApplicationURI("lease/{$id}/logs/query/all/"));
$crumbs = $this->buildApplicationCrumbs();
$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($commands, pht('Commands'));
- $log_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Lease Logs'))
- ->setTable($log_table);
-
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$log_box,
),
array(
'title' => $title,
));
}
private function buildActionListView(DrydockLease $lease) {
$viewer = $this->getViewer();
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($lease);
$id = $lease->getID();
$can_release = $lease->canRelease();
if ($lease->isReleasing()) {
$can_release = false;
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$lease,
PhabricatorPolicyCapability::CAN_EDIT);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Release Lease'))
->setIcon('fa-times')
->setHref($this->getApplicationURI("/lease/{$id}/release/"))
->setWorkflow(true)
->setDisabled(!$can_release || !$can_edit));
return $view;
}
private function buildPropertyListView(
DrydockLease $lease,
PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = new PHUIPropertyListView();
$view->setActionList($actions);
$view->addProperty(
pht('Status'),
DrydockLeaseStatus::getNameForStatus($lease->getStatus()));
$view->addProperty(
pht('Resource Type'),
$lease->getResourceType());
$owner_phid = $lease->getOwnerPHID();
if ($owner_phid) {
$owner_display = $viewer->renderHandle($owner_phid);
} else {
$owner_display = phutil_tag('em', array(), pht('No Owner'));
}
$view->addProperty(pht('Owner'), $owner_display);
$resource_phid = $lease->getResourcePHID();
if ($resource_phid) {
$resource_display = $viewer->renderHandle($resource_phid);
} else {
$resource_display = phutil_tag('em', array(), pht('No Resource'));
}
$view->addProperty(pht('Resource'), $resource_display);
$until = $lease->getUntil();
if ($until) {
$until_display = phabricator_datetime($until, $viewer);
} else {
$until_display = phutil_tag('em', array(), pht('Never'));
}
$view->addProperty(pht('Expires'), $until_display);
$attributes = $lease->getAttributes();
if ($attributes) {
$view->addSectionHeader(
pht('Attributes'), 'fa-list-ul');
foreach ($attributes as $key => $value) {
$view->addProperty($key, $value);
}
}
return $view;
}
}
diff --git a/src/applications/drydock/controller/DrydockLogController.php b/src/applications/drydock/controller/DrydockLogController.php
index 0e28c62cb4..5ae87e6aad 100644
--- a/src/applications/drydock/controller/DrydockLogController.php
+++ b/src/applications/drydock/controller/DrydockLogController.php
@@ -1,27 +1,119 @@
<?php
abstract class DrydockLogController
extends DrydockController {
+ private $blueprint;
+ private $resource;
+ private $lease;
+
+ public function setBlueprint(DrydockBlueprint $blueprint) {
+ $this->blueprint = $blueprint;
+ return $this;
+ }
+
+ public function getBlueprint() {
+ return $this->blueprint;
+ }
+
+ public function setResource(DrydockResource $resource) {
+ $this->resource = $resource;
+ return $this;
+ }
+
+ public function getResource() {
+ return $this->resource;
+ }
+
+ public function setLease(DrydockLease $lease) {
+ $this->lease = $lease;
+ return $this;
+ }
+
+ public function getLease() {
+ return $this->lease;
+ }
+
public function buildSideNavView() {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
- id(new DrydockLogSearchEngine())
- ->setViewer($this->getRequest()->getUser())
- ->addNavigationItems($nav->getMenu());
+ $engine = id(new DrydockLogSearchEngine())
+ ->setViewer($this->getRequest()->getUser());
+
+ $blueprint = $this->getBlueprint();
+ if ($blueprint) {
+ $engine->setBlueprint($blueprint);
+ }
+
+ $resource = $this->getResource();
+ if ($resource) {
+ $engine->setResource($resource);
+ }
+
+ $lease = $this->getLease();
+ if ($lease) {
+ $engine->setLease($lease);
+ }
+
+ $engine->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
- $crumbs->addTextCrumb(
- pht('Logs'),
- $this->getApplicationURI('log/'));
+
+ $blueprint = $this->getBlueprint();
+ $resource = $this->getResource();
+ $lease = $this->getLease();
+ if ($blueprint) {
+ $id = $blueprint->getID();
+
+ $crumbs->addTextCrumb(
+ pht('Blueprints'),
+ $this->getApplicationURI('blueprint/'));
+
+ $crumbs->addTextCrumb(
+ $blueprint->getBlueprintName(),
+ $this->getApplicationURI("blueprint/{$id}/"));
+
+ $crumbs->addTextCrumb(
+ pht('Logs'),
+ $this->getApplicationURI("blueprint/{$id}/logs/"));
+ } else if ($resource) {
+ $id = $resource->getID();
+
+ $crumbs->addTextCrumb(
+ pht('Resources'),
+ $this->getApplicationURI('resource/'));
+
+ $crumbs->addTextCrumb(
+ $resource->getName(),
+ $this->getApplicationURI("resource/{$id}/"));
+
+ $crumbs->addTextCrumb(
+ pht('Logs'),
+ $this->getApplicationURI("resource/{$id}/logs/"));
+ } else if ($lease) {
+ $id = $lease->getID();
+
+ $crumbs->addTextCrumb(
+ pht('Leases'),
+ $this->getApplicationURI('lease/'));
+
+ $crumbs->addTextCrumb(
+ $lease->getLeaseName(),
+ $this->getApplicationURI("lease/{$id}/"));
+
+ $crumbs->addTextCrumb(
+ pht('Logs'),
+ $this->getApplicationURI("lease/{$id}/logs/"));
+ }
+
return $crumbs;
}
}
diff --git a/src/applications/drydock/controller/DrydockLogListController.php b/src/applications/drydock/controller/DrydockLogListController.php
index aecf77dc77..b5e4d4ff0c 100644
--- a/src/applications/drydock/controller/DrydockLogListController.php
+++ b/src/applications/drydock/controller/DrydockLogListController.php
@@ -1,21 +1,63 @@
<?php
final class DrydockLogListController extends DrydockLogController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
- $querykey = $request->getURIData('queryKey');
+ $engine = new DrydockLogSearchEngine();
+
+ $id = $request->getURIData('id');
+ $type = $request->getURIData('type');
+ switch ($type) {
+ case 'blueprint':
+ $blueprint = id(new DrydockBlueprintQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$blueprint) {
+ return new Aphront404Response();
+ }
+ $engine->setBlueprint($blueprint);
+ $this->setBlueprint($blueprint);
+ break;
+ case 'resource':
+ $resource = id(new DrydockResourceQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$resource) {
+ return new Aphront404Response();
+ }
+ $engine->setResource($resource);
+ $this->setResource($resource);
+ break;
+ case 'lease':
+ $lease = id(new DrydockLeaseQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->executeOne();
+ if (!$lease) {
+ return new Aphront404Response();
+ }
+ $engine->setLease($lease);
+ $this->setLease($lease);
+ break;
+ default:
+ return new Aphront404Response();
+ }
+
+ $query_key = $request->getURIData('queryKey');
$controller = id(new PhabricatorApplicationSearchController())
- ->setQueryKey($querykey)
- ->setSearchEngine(new DrydockLogSearchEngine())
+ ->setQueryKey($query_key)
+ ->setSearchEngine($engine)
->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller);
}
}
diff --git a/src/applications/drydock/controller/DrydockResourceViewController.php b/src/applications/drydock/controller/DrydockResourceViewController.php
index 23f81c5225..f97081e673 100644
--- a/src/applications/drydock/controller/DrydockResourceViewController.php
+++ b/src/applications/drydock/controller/DrydockResourceViewController.php
@@ -1,195 +1,183 @@
<?php
final class DrydockResourceViewController extends DrydockResourceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$resource = id(new DrydockResourceQuery())
->setViewer($viewer)
->withIDs(array($id))
->needUnconsumedCommands(true)
->executeOne();
if (!$resource) {
return new Aphront404Response();
}
$title = pht('Resource %s %s', $resource->getID(), $resource->getName());
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setPolicyObject($resource)
->setHeader($title);
if ($resource->isReleasing()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Releasing'));
}
$actions = $this->buildActionListView($resource);
$properties = $this->buildPropertyListView($resource, $actions);
- $resource_uri = 'resource/'.$resource->getID().'/';
- $resource_uri = $this->getApplicationURI($resource_uri);
+ $id = $resource->getID();
+ $resource_uri = $this->getApplicationURI("resource/{$id}/");
- $pager = new PHUIPagerView();
- $pager->setURI(new PhutilURI($resource_uri), 'offset');
- $pager->setOffset($request->getInt('offset'));
+ $log_query = id(new DrydockLogQuery())
+ ->withResourcePHIDs(array($resource->getPHID()));
- $logs = id(new DrydockLogQuery())
- ->setViewer($viewer)
- ->withResourceIDs(array($resource->getID()))
- ->executeWithOffsetPager($pager);
-
- $log_table = id(new DrydockLogListView())
- ->setUser($viewer)
- ->setLogs($logs)
- ->render();
- $log_table->appendChild($pager);
+ $log_box = $this->buildLogBox(
+ $log_query,
+ $this->getApplicationURI("resource/{$id}/logs/query/all/"));
$crumbs = $this->buildApplicationCrumbs();
$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($commands, pht('Commands'));
$lease_box = $this->buildLeaseBox($resource);
- $log_box = id(new PHUIObjectBoxView())
- ->setHeaderText(pht('Resource Logs'))
- ->setTable($log_table);
-
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$lease_box,
$log_box,
),
array(
'title' => $title,
));
}
private function buildActionListView(DrydockResource $resource) {
$viewer = $this->getViewer();
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($resource);
$can_release = $resource->canRelease();
if ($resource->isReleasing()) {
$can_release = false;
}
$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('Release Resource'))
->setIcon('fa-times')
->setWorkflow(true)
->setDisabled(!$can_release || !$can_edit));
return $view;
}
private function buildPropertyListView(
DrydockResource $resource,
PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setActionList($actions);
$status = $resource->getStatus();
$status = DrydockResourceStatus::getNameForStatus($status);
$view->addProperty(
pht('Status'),
$status);
$until = $resource->getUntil();
if ($until) {
$until_display = phabricator_datetime($until, $viewer);
} else {
$until_display = phutil_tag('em', array(), pht('Never'));
}
$view->addProperty(pht('Expires'), $until_display);
$view->addProperty(
pht('Resource Type'),
$resource->getType());
$view->addProperty(
pht('Blueprint'),
$viewer->renderHandle($resource->getBlueprintPHID()));
$attributes = $resource->getAttributes();
if ($attributes) {
$view->addSectionHeader(
pht('Attributes'), 'fa-list-ul');
foreach ($attributes as $key => $value) {
$view->addProperty($key, $value);
}
}
return $view;
}
private function buildLeaseBox(DrydockResource $resource) {
$viewer = $this->getViewer();
$leases = id(new DrydockLeaseQuery())
->setViewer($viewer)
->withResourcePHIDs(array($resource->getPHID()))
->withStatuses(
array(
DrydockLeaseStatus::STATUS_PENDING,
DrydockLeaseStatus::STATUS_ACQUIRED,
DrydockLeaseStatus::STATUS_ACTIVE,
))
->setLimit(100)
->execute();
$id = $resource->getID();
$leases_uri = "resource/{$id}/leases/query/all/";
$leases_uri = $this->getApplicationURI($leases_uri);
$lease_header = id(new PHUIHeaderView())
->setHeader(pht('Active Leases'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($leases_uri)
->setIconFont('fa-search')
->setText(pht('View All Leases')));
$lease_list = id(new DrydockLeaseListView())
->setUser($viewer)
->setLeases($leases)
->render()
->setNoDataString(pht('This resource has no active leases.'));
return id(new PHUIObjectBoxView())
->setHeader($lease_header)
->setObjectList($lease_list);
}
}
diff --git a/src/applications/drydock/query/DrydockLogQuery.php b/src/applications/drydock/query/DrydockLogQuery.php
index 47a6795463..00980edb4d 100644
--- a/src/applications/drydock/query/DrydockLogQuery.php
+++ b/src/applications/drydock/query/DrydockLogQuery.php
@@ -1,113 +1,126 @@
<?php
final class DrydockLogQuery extends DrydockQuery {
- private $resourceIDs;
- private $leaseIDs;
+ private $blueprintPHIDs;
+ private $resourcePHIDs;
+ private $leasePHIDs;
- public function withResourceIDs(array $ids) {
- $this->resourceIDs = $ids;
+ public function withBlueprintPHIDs(array $phids) {
+ $this->blueprintPHIDs = $phids;
return $this;
}
- public function withLeaseIDs(array $ids) {
- $this->leaseIDs = $ids;
+ public function withResourcePHIDs(array $phids) {
+ $this->resourcePHIDs = $phids;
return $this;
}
+ public function withLeasePHIDs(array $phids) {
+ $this->leasePHIDs = $phids;
+ return $this;
+ }
+
+ public function newResultObject() {
+ return new DrydockLog();
+ }
+
protected function loadPage() {
- $table = new DrydockLog();
- $conn_r = $table->establishConnection('r');
-
- $data = queryfx_all(
- $conn_r,
- 'SELECT log.* FROM %T log %Q %Q %Q',
- $table->getTableName(),
- $this->buildWhereClause($conn_r),
- $this->buildOrderClause($conn_r),
- $this->buildLimitClause($conn_r));
-
- return $table->loadAllFromArray($data);
+ return $this->loadStandardPage($this->newResultObject());
}
- protected function willFilterPage(array $logs) {
- $resource_ids = array_filter(mpull($logs, 'getResourceID'));
- if ($resource_ids) {
+ protected function didFilterPage(array $logs) {
+ $blueprint_phids = array_filter(mpull($logs, 'getBlueprintPHID'));
+ if ($blueprint_phids) {
+ $blueprints = id(new DrydockBlueprintQuery())
+ ->setParentQuery($this)
+ ->setViewer($this->getViewer())
+ ->withPHIDs($blueprint_phids)
+ ->execute();
+ $blueprints = mpull($blueprints, null, 'getPHID');
+ } else {
+ $blueprints = array();
+ }
+
+ foreach ($logs as $key => $log) {
+ $blueprint = null;
+ $blueprint_phid = $log->getBlueprintPHID();
+ if ($blueprint_phid) {
+ $blueprint = idx($blueprints, $blueprint_phid);
+ }
+ $log->attachBlueprint($blueprint);
+ }
+
+ $resource_phids = array_filter(mpull($logs, 'getResourcePHID'));
+ if ($resource_phids) {
$resources = id(new DrydockResourceQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
- ->withIDs(array_unique($resource_ids))
+ ->withPHIDs($resource_phids)
->execute();
+ $resources = mpull($resources, null, 'getPHID');
} else {
$resources = array();
}
foreach ($logs as $key => $log) {
$resource = null;
- if ($log->getResourceID()) {
- $resource = idx($resources, $log->getResourceID());
- if (!$resource) {
- unset($logs[$key]);
- continue;
- }
+ $resource_phid = $log->getResourcePHID();
+ if ($resource_phid) {
+ $resource = idx($resources, $resource_phid);
}
$log->attachResource($resource);
}
- $lease_ids = array_filter(mpull($logs, 'getLeaseID'));
- if ($lease_ids) {
+ $lease_phids = array_filter(mpull($logs, 'getLeasePHID'));
+ if ($lease_phids) {
$leases = id(new DrydockLeaseQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
- ->withIDs(array_unique($lease_ids))
+ ->withPHIDs($lease_phids)
->execute();
+ $leases = mpull($leases, null, 'getPHID');
} else {
$leases = array();
}
foreach ($logs as $key => $log) {
$lease = null;
- if ($log->getLeaseID()) {
- $lease = idx($leases, $log->getLeaseID());
- if (!$lease) {
- unset($logs[$key]);
- continue;
- }
+ $lease_phid = $log->getLeasePHID();
+ if ($lease_phid) {
+ $lease = idx($leases, $lease_phid);
}
$log->attachLease($lease);
}
- // These logs are meaningless and their policies aren't computable. They
- // shouldn't exist, but throw them away if they do.
- foreach ($logs as $key => $log) {
- if (!$log->getResource() && !$log->getLease()) {
- unset($logs[$key]);
- }
- }
-
return $logs;
}
- protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
- $where = array();
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
- if ($this->resourceIDs !== null) {
+ if ($this->blueprintPHIDs !== null) {
$where[] = qsprintf(
- $conn_r,
- 'resourceID IN (%Ld)',
- $this->resourceIDs);
+ $conn,
+ 'blueprintPHID IN (%Ls)',
+ $this->blueprintPHIDs);
}
- if ($this->leaseIDs !== null) {
+ if ($this->resourcePHIDs !== null) {
$where[] = qsprintf(
- $conn_r,
- 'leaseID IN (%Ld)',
- $this->leaseIDs);
+ $conn,
+ 'resourcePHID IN (%Ls)',
+ $this->resourcePHIDs);
}
- $where[] = $this->buildPagingClause($conn_r);
+ if ($this->leasePHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'leasePHID IN (%Ls)',
+ $this->leasePHIDs);
+ }
- return $this->formatWhereClause($where);
+ return $where;
}
}
diff --git a/src/applications/drydock/query/DrydockLogSearchEngine.php b/src/applications/drydock/query/DrydockLogSearchEngine.php
index 13777031d6..43b1511c01 100644
--- a/src/applications/drydock/query/DrydockLogSearchEngine.php
+++ b/src/applications/drydock/query/DrydockLogSearchEngine.php
@@ -1,117 +1,138 @@
<?php
final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine {
- public function getResultTypeDescription() {
- return pht('Drydock Logs');
+ private $blueprint;
+ private $resource;
+ private $lease;
+
+ public function setBlueprint(DrydockBlueprint $blueprint) {
+ $this->blueprint = $blueprint;
+ return $this;
}
- public function getApplicationClassName() {
- return 'PhabricatorDrydockApplication';
+ public function getBlueprint() {
+ return $this->blueprint;
}
- public function buildSavedQueryFromRequest(AphrontRequest $request) {
- $query = new PhabricatorSavedQuery();
+ public function setResource(DrydockResource $resource) {
+ $this->resource = $resource;
+ return $this;
+ }
- $query->setParameter(
- 'resourcePHIDs',
- $this->readListFromRequest($request, 'resources'));
- $query->setParameter(
- 'leasePHIDs',
- $this->readListFromRequest($request, 'leases'));
+ public function getResource() {
+ return $this->resource;
+ }
- return $query;
+ public function setLease(DrydockLease $lease) {
+ $this->lease = $lease;
+ return $this;
}
- public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
- $resource_phids = $saved->getParameter('resourcePHIDs', array());
- $lease_phids = $saved->getParameter('leasePHIDs', array());
+ public function getLease() {
+ return $this->lease;
+ }
- // TODO: Change logs to use PHIDs instead of IDs.
- $resource_ids = array();
- $lease_ids = array();
+ public function canUseInPanelContext() {
+ // Prevent use on Dashboard panels since all log queries currently need a
+ // parent object and these don't seem particularly useful in any case.
+ return false;
+ }
- if ($resource_phids) {
- $resource_ids = id(new DrydockResourceQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs($resource_phids)
- ->execute();
- $resource_ids = mpull($resource_ids, 'getID');
- }
+ public function getResultTypeDescription() {
+ return pht('Drydock Logs');
+ }
- if ($lease_phids) {
- $lease_ids = id(new DrydockLeaseQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
- ->withPHIDs($lease_phids)
- ->execute();
- $lease_ids = mpull($lease_ids, 'getID');
- }
+ public function getApplicationClassName() {
+ return 'PhabricatorDrydockApplication';
+ }
+ public function newQuery() {
$query = new DrydockLogQuery();
- if ($resource_ids) {
- $query->withResourceIDs($resource_ids);
+
+ $blueprint = $this->getBlueprint();
+ if ($blueprint) {
+ $query->withBlueprintPHIDs(array($blueprint->getPHID()));
+ }
+
+ $resource = $this->getResource();
+ if ($resource) {
+ $query->withResourcePHIDs(array($resource->getPHID()));
}
- if ($lease_ids) {
- $query->withLeaseIDs($lease_ids);
+
+ $lease = $this->getLease();
+ if ($lease) {
+ $query->withLeasePHIDs(array($lease->getPHID()));
}
return $query;
}
- public function buildSearchForm(
- AphrontFormView $form,
- PhabricatorSavedQuery $saved) {
+ protected function buildQueryFromParameters(array $map) {
+ $query = $this->newQuery();
- $form
- ->appendControl(
- id(new AphrontFormTokenizerControl())
- ->setDatasource(new DrydockResourceDatasource())
- ->setName('resources')
- ->setLabel(pht('Resources'))
- ->setValue($saved->getParameter('resourcePHIDs', array())))
- ->appendControl(
- id(new AphrontFormTokenizerControl())
- ->setDatasource(new DrydockLeaseDatasource())
- ->setName('leases')
- ->setLabel(pht('Leases'))
- ->setValue($saved->getParameter('leasePHIDs', array())));
+ return $query;
+ }
+
+ protected function buildCustomSearchFields() {
+ return array();
}
protected function getURI($path) {
- return '/drydock/log/'.$path;
+ $blueprint = $this->getBlueprint();
+ if ($blueprint) {
+ $id = $blueprint->getID();
+ return "/drydock/blueprint/{$id}/logs/{$path}";
+ }
+
+ $resource = $this->getResource();
+ if ($resource) {
+ $id = $resource->getID();
+ return "/drydock/resource/{$id}/logs/{$path}";
+ }
+
+ $lease = $this->getLease();
+ if ($lease) {
+ $id = $lease->getID();
+ return "/drydock/lease/{$id}/logs/{$path}";
+ }
+
+ throw new Exception(
+ pht(
+ 'Search engine has no blueprint, resource, or lease.'));
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Logs'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $logs,
PhabricatorSavedQuery $query,
array $handles) {
$list = id(new DrydockLogListView())
->setUser($this->requireViewer())
->setLogs($logs);
$result = new PhabricatorApplicationSearchResultView();
$result->setTable($list);
return $result;
}
}
diff --git a/src/applications/drydock/storage/DrydockLease.php b/src/applications/drydock/storage/DrydockLease.php
index af0b322b62..b16efbabbb 100644
--- a/src/applications/drydock/storage/DrydockLease.php
+++ b/src/applications/drydock/storage/DrydockLease.php
@@ -1,406 +1,427 @@
<?php
final class DrydockLease extends DrydockDAO
implements PhabricatorPolicyInterface {
protected $resourcePHID;
protected $resourceType;
protected $until;
protected $ownerPHID;
protected $attributes = array();
protected $status = DrydockLeaseStatus::STATUS_PENDING;
private $resource = self::ATTACHABLE;
private $unconsumedCommands = self::ATTACHABLE;
private $releaseOnDestruction;
private $isAcquired = false;
private $isActivated = false;
private $activateWhenAcquired = false;
private $slotLocks = array();
/**
* 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) {
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() {
return pht('Lease %d', $this->getID());
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'attributes' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'status' => 'text32',
'until' => 'epoch?',
'resourceType' => 'text128',
'ownerPHID' => 'phid?',
'resourcePHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_resource' => array(
'columns' => array('resourcePHID', 'status'),
),
),
) + 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(DrydockLeasePHIDType::TYPECONST);
}
public function getInterface($type) {
return $this->getResource()->getInterface($this, $type);
}
public function getResource() {
return $this->assertAttached($this->resource);
}
public function attachResource(DrydockResource $resource = null) {
$this->resource = $resource;
return $this;
}
public function hasAttachedResource() {
return ($this->resource !== null);
}
public function getUnconsumedCommands() {
return $this->assertAttached($this->unconsumedCommands);
}
public function attachUnconsumedCommands(array $commands) {
$this->unconsumedCommands = $commands;
return $this;
}
public function isReleasing() {
foreach ($this->getUnconsumedCommands() as $command) {
if ($command->getCommand() == DrydockCommand::COMMAND_RELEASE) {
return true;
}
}
return false;
}
public function queueForActivation() {
if ($this->getID()) {
throw new Exception(
pht('Only new leases may be queued for activation!'));
}
$this
->setStatus(DrydockLeaseStatus::STATUS_PENDING)
->save();
$task = PhabricatorWorker::scheduleTask(
'DrydockAllocatorWorker',
array(
'leasePHID' => $this->getPHID(),
),
array(
'objectPHID' => $this->getPHID(),
));
return $this;
}
public function isActivating() {
switch ($this->getStatus()) {
case DrydockLeaseStatus::STATUS_PENDING:
case DrydockLeaseStatus::STATUS_ACQUIRED:
return true;
}
return false;
}
public function isActive() {
switch ($this->getStatus()) {
case DrydockLeaseStatus::STATUS_ACTIVE:
return true;
}
return false;
}
public function waitUntilActive() {
while (true) {
$lease = $this->reload();
if (!$lease) {
throw new Exception(pht('Failed to reload lease.'));
}
$status = $lease->getStatus();
switch ($status) {
case DrydockLeaseStatus::STATUS_ACTIVE:
return;
case DrydockLeaseStatus::STATUS_RELEASED:
throw new Exception(pht('Lease has already been released!'));
case DrydockLeaseStatus::STATUS_DESTROYED:
throw new Exception(pht('Lease has already been destroyed!'));
case DrydockLeaseStatus::STATUS_BROKEN:
throw new Exception(pht('Lease has been broken!'));
case DrydockLeaseStatus::STATUS_PENDING:
case DrydockLeaseStatus::STATUS_ACQUIRED:
break;
default:
throw new Exception(
pht(
'Lease has unknown status "%s".',
$status));
}
sleep(1);
}
}
public function setActivateWhenAcquired($activate) {
$this->activateWhenAcquired = true;
return $this;
}
public function needSlotLock($key) {
$this->slotLocks[] = $key;
return $this;
}
public function acquireOnResource(DrydockResource $resource) {
$expect_status = DrydockLeaseStatus::STATUS_PENDING;
$actual_status = $this->getStatus();
if ($actual_status != $expect_status) {
throw new Exception(
pht(
'Trying to acquire a lease on a resource which is in the wrong '.
'state: status must be "%s", actually "%s".',
$expect_status,
$actual_status));
}
if ($this->activateWhenAcquired) {
$new_status = DrydockLeaseStatus::STATUS_ACTIVE;
} else {
$new_status = DrydockLeaseStatus::STATUS_ACQUIRED;
}
if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) {
if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) {
throw new Exception(
pht(
'Trying to acquire an active lease on a pending resource. '.
'You can not immediately activate leases on resources which '.
'need time to start up.'));
}
}
$this->openTransaction();
$this
->setResourcePHID($resource->getPHID())
->setStatus($new_status)
->save();
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
$this->slotLocks = array();
$this->saveTransaction();
$this->isAcquired = true;
if ($new_status == DrydockLeaseStatus::STATUS_ACTIVE) {
$this->didActivate();
}
return $this;
}
public function isAcquiredLease() {
return $this->isAcquired;
}
public function activateOnResource(DrydockResource $resource) {
$expect_status = DrydockLeaseStatus::STATUS_ACQUIRED;
$actual_status = $this->getStatus();
if ($actual_status != $expect_status) {
throw new Exception(
pht(
'Trying to activate a lease which has the wrong status: status '.
'must be "%s", actually "%s".',
$expect_status,
$actual_status));
}
if ($resource->getStatus() == DrydockResourceStatus::STATUS_PENDING) {
// TODO: Be stricter about this?
throw new Exception(
pht(
'Trying to activate a lease on a pending resource.'));
}
$this->openTransaction();
$this
->setStatus(DrydockLeaseStatus::STATUS_ACTIVE)
->save();
DrydockSlotLock::acquireLocks($this->getPHID(), $this->slotLocks);
$this->slotLocks = array();
$this->saveTransaction();
$this->isActivated = true;
$this->didActivate();
return $this;
}
public function isActivatedLease() {
return $this->isActivated;
}
public function canRelease() {
if (!$this->getID()) {
return false;
}
switch ($this->getStatus()) {
case DrydockLeaseStatus::STATUS_RELEASED:
case DrydockLeaseStatus::STATUS_DESTROYED:
return false;
default:
return true;
}
}
public function canUpdate() {
switch ($this->getStatus()) {
case DrydockLeaseStatus::STATUS_ACTIVE:
return true;
default:
return false;
}
}
public function scheduleUpdate($epoch = null) {
PhabricatorWorker::scheduleTask(
'DrydockLeaseUpdateWorker',
array(
'leasePHID' => $this->getPHID(),
'isExpireTask' => ($epoch !== null),
),
array(
'objectPHID' => $this->getPHID(),
'delayUntil' => ($epoch ? (int)$epoch : null),
));
}
public function setAwakenTaskIDs(array $ids) {
$this->setAttribute('internal.awakenTaskIDs', $ids);
return $this;
}
private function didActivate() {
$viewer = PhabricatorUser::getOmnipotentUser();
$need_update = false;
+ // TODO: This is just a placeholder to get some data in the table.
+ $this->logEvent('activated');
+
$commands = id(new DrydockCommandQuery())
->setViewer($viewer)
->withTargetPHIDs(array($this->getPHID()))
->withConsumed(false)
->execute();
if ($commands) {
$need_update = true;
}
if ($need_update) {
$this->scheduleUpdate();
}
$expires = $this->getUntil();
if ($expires) {
$this->scheduleUpdate($expires);
}
$awaken_ids = $this->getAttribute('internal.awakenTaskIDs');
if (is_array($awaken_ids) && $awaken_ids) {
PhabricatorWorker::awakenTaskIDs($awaken_ids);
}
}
+ public function logEvent($type, array $data = array()) {
+ $log = id(new DrydockLog())
+ ->setEpoch(PhabricatorTime::getNow())
+ ->setType($type)
+ ->setData($data);
+
+ $log->setLeasePHID($this->getPHID());
+
+ $resource = $this->getResource();
+ if ($resource) {
+ $log->setResourcePHID($resource->getPHID());
+ $log->setBlueprintPHID($resource->getBlueprintPHID());
+ }
+
+ return $log->save();
+ }
+
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
if ($this->getResource()) {
return $this->getResource()->getPolicy($capability);
}
// TODO: Implement reasonable policies.
return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getResource()) {
return $this->getResource()->hasAutomaticCapability($capability, $viewer);
}
return false;
}
public function describeAutomaticCapability($capability) {
return pht('Leases inherit policies from the resources they lease.');
}
}
diff --git a/src/applications/drydock/storage/DrydockLog.php b/src/applications/drydock/storage/DrydockLog.php
index 36d310c510..5d75d82c65 100644
--- a/src/applications/drydock/storage/DrydockLog.php
+++ b/src/applications/drydock/storage/DrydockLog.php
@@ -1,82 +1,115 @@
<?php
final class DrydockLog extends DrydockDAO
implements PhabricatorPolicyInterface {
- protected $resourceID;
- protected $leaseID;
+ protected $blueprintPHID;
+ protected $resourcePHID;
+ protected $leasePHID;
protected $epoch;
- protected $message;
+ protected $type;
+ protected $data = array();
+ private $blueprint = self::ATTACHABLE;
private $resource = self::ATTACHABLE;
private $lease = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
+ self::CONFIG_SERIALIZATION => array(
+ 'data' => self::SERIALIZATION_JSON,
+ ),
self::CONFIG_COLUMN_SCHEMA => array(
- 'resourceID' => 'id?',
- 'leaseID' => 'id?',
- 'message' => 'text',
+ 'blueprintPHID' => 'phid?',
+ 'resourcePHID' => 'phid?',
+ 'leasePHID' => 'phid?',
+ 'type' => 'text64',
),
self::CONFIG_KEY_SCHEMA => array(
- 'resourceID' => array(
- 'columns' => array('resourceID', 'epoch'),
+ 'key_blueprint' => array(
+ 'columns' => array('blueprintPHID', 'type'),
+ ),
+ 'key_resource' => array(
+ 'columns' => array('resourcePHID', 'type'),
),
- 'leaseID' => array(
- 'columns' => array('leaseID', 'epoch'),
+ 'key_lease' => array(
+ 'columns' => array('leasePHID', 'type'),
),
'epoch' => array(
'columns' => array('epoch'),
),
),
) + parent::getConfiguration();
}
+ public function attachBlueprint(DrydockBlueprint $blueprint = null) {
+ $this->blueprint = $blueprint;
+ return $this;
+ }
+
+ public function getBlueprint() {
+ return $this->assertAttached($this->blueprint);
+ }
+
public function attachResource(DrydockResource $resource = null) {
$this->resource = $resource;
return $this;
}
public function getResource() {
return $this->assertAttached($this->resource);
}
public function attachLease(DrydockLease $lease = null) {
$this->lease = $lease;
return $this;
}
public function getLease() {
return $this->assertAttached($this->lease);
}
+ public function isComplete() {
+ if ($this->getBlueprintPHID() && !$this->getBlueprint()) {
+ return false;
+ }
+
+ if ($this->getResourcePHID() && !$this->getResource()) {
+ return false;
+ }
+
+ if ($this->getLeasePHID() && !$this->getLease()) {
+ return false;
+ }
+
+ return true;
+ }
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
public function getPolicy($capability) {
- if ($this->getResource()) {
- return $this->getResource()->getPolicy($capability);
- }
- return $this->getLease()->getPolicy($capability);
+ // NOTE: We let you see that logs exist no matter what, but don't actually
+ // show you log content unless you can see all of the associated objects.
+ return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
- if ($this->getResource()) {
- return $this->getResource()->hasAutomaticCapability($capability, $viewer);
- }
- return $this->getLease()->hasAutomaticCapability($capability, $viewer);
+ return false;
}
public function describeAutomaticCapability($capability) {
- return pht('Logs inherit the policy of their resources.');
+ return pht(
+ 'To view log details, you must be able to view the associated '.
+ 'blueprint, resource and lease.');
}
}
diff --git a/src/applications/drydock/view/DrydockLogListView.php b/src/applications/drydock/view/DrydockLogListView.php
index 22e280939b..f6560270a0 100644
--- a/src/applications/drydock/view/DrydockLogListView.php
+++ b/src/applications/drydock/view/DrydockLogListView.php
@@ -1,74 +1,88 @@
<?php
final class DrydockLogListView extends AphrontView {
private $logs;
public function setLogs(array $logs) {
assert_instances_of($logs, 'DrydockLog');
$this->logs = $logs;
return $this;
}
public function render() {
$logs = $this->logs;
$viewer = $this->getUser();
$view = new PHUIObjectItemListView();
$rows = array();
foreach ($logs as $log) {
- $resource_uri = '/drydock/resource/'.$log->getResourceID().'/';
- $lease_uri = '/drydock/lease/'.$log->getLeaseID().'/';
+ $blueprint_phid = $log->getBlueprintPHID();
+ if ($blueprint_phid) {
+ $blueprint = $viewer->renderHandle($blueprint_phid);
+ } else {
+ $blueprint = null;
+ }
+
+ $resource_phid = $log->getResourcePHID();
+ if ($resource_phid) {
+ $resource = $viewer->renderHandle($resource_phid);
+ } else {
+ $resource = null;
+ }
+
+ $lease_phid = $log->getLeasePHID();
+ if ($lease_phid) {
+ $lease = $viewer->renderHandle($lease_phid);
+ } else {
+ $lease = null;
+ }
- $resource_name = $log->getResourceID();
- if ($log->getResourceID() !== null) {
- $resource_name = $log->getResource()->getName();
+ if ($log->isComplete()) {
+ // TODO: This is a placeholder.
+ $type = $log->getType();
+ $data = print_r($log->getData(), true);
+ } else {
+ $type = phutil_tag('em', array(), pht('Restricted'));
+ $data = phutil_tag(
+ 'em',
+ array(),
+ pht('You do not have permission to view this log event.'));
}
$rows[] = array(
- phutil_tag(
- 'a',
- array(
- 'href' => $resource_uri,
- ),
- $resource_name),
- phutil_tag(
- 'a',
- array(
- 'href' => $lease_uri,
- ),
- $log->getLeaseID()),
- $log->getMessage(),
+ $blueprint,
+ $resource,
+ $lease,
+ $type,
+ $data,
phabricator_datetime($log->getEpoch(), $viewer),
);
}
$table = new AphrontTableView($rows);
$table->setDeviceReadyTable(true);
$table->setHeaders(
array(
+ pht('Blueprint'),
pht('Resource'),
pht('Lease'),
- pht('Message'),
+ pht('Type'),
+ pht('Data'),
pht('Date'),
));
- $table->setShortHeaders(
- array(
- pht('R'),
- pht('L'),
- pht('Message'),
- '',
- ));
$table->setColumnClasses(
array(
+ '',
+ '',
'',
'',
'wide',
'',
));
return $table;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 5:54 PM (1 d, 19 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
140661
Default Alt Text
(69 KB)

Event Timeline