1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 19:40:55 +01:00

Move Drydock logs to PHIDs and increased structure

Summary:
Ref T9252. Several general changes here:

  - Moves logs to use PHIDs instead of IDs. This generally improves flexibility (for example, it's a lot easier to render handles).
  - Adds `blueprintPHID` to logs. Although you can usually figure this out from the leasePHID or resourcePHID, it lets us query relevant logs on Blueprint views.
  - Instead of making logs a top-level object, make them strictly a sub-object of Blueprints, Resources and Leases. So you go Drydock > Lease > Logs, etc., to get to logs.
    - I might restore the "everything" view eventually, but it doesn't interact well with policies and I'm not sure it's very useful. A policy-violating `bin/drydock log` might be cleaner.
  - Policy-wise, we always show you that logs exist, we just don't show you log content if it's about something you can't see. This is similar to seeing restricted handles in other applications.
  - Instead of just having a message, give logs "type" + "data". This will let logs be more structured and translatable. This is similar to recent changes to Herald which seem to have worked well.

Test Plan:
Added some placeholder log writes, viewed those logs in the UI.

{F855199}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9252

Differential Revision: https://secure.phabricator.com/D14196
This commit is contained in:
epriestley 2015-10-01 08:06:23 -07:00
parent 98006f2cf3
commit 2ef5b5321d
14 changed files with 488 additions and 252 deletions

View file

@ -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};

View file

@ -47,7 +47,7 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
return array( return array(
'/drydock/' => array( '/drydock/' => array(
'' => 'DrydockConsoleController', '' => 'DrydockConsoleController',
'blueprint/' => array( '(?P<type>blueprint)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockBlueprintListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockBlueprintListController',
'(?P<id>[1-9]\d*)/' => array( '(?P<id>[1-9]\d*)/' => array(
'' => 'DrydockBlueprintViewController', '' => 'DrydockBlueprintViewController',
@ -55,29 +55,32 @@ final class PhabricatorDrydockApplication extends PhabricatorApplication {
'DrydockBlueprintDisableController', 'DrydockBlueprintDisableController',
'resources/(?:query/(?P<queryKey>[^/]+)/)?' => 'resources/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockResourceListController', 'DrydockResourceListController',
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockLogListController',
), ),
'create/' => 'DrydockBlueprintCreateController', 'create/' => 'DrydockBlueprintCreateController',
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController', 'edit/(?:(?P<id>[1-9]\d*)/)?' => 'DrydockBlueprintEditController',
), ),
'resource/' => array( '(?P<type>resource)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockResourceListController',
'(?P<id>[1-9]\d*)/' => array( '(?P<id>[1-9]\d*)/' => array(
'' => 'DrydockResourceViewController', '' => 'DrydockResourceViewController',
'release/' => 'DrydockResourceReleaseController', 'release/' => 'DrydockResourceReleaseController',
'leases/(?:query/(?P<queryKey>[^/]+)/)?' => 'leases/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockLeaseListController', 'DrydockLeaseListController',
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockLogListController',
), ),
), ),
'lease/' => array( '(?P<type>lease)/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLeaseListController', '(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLeaseListController',
'(?P<id>[1-9]\d*)/' => array( '(?P<id>[1-9]\d*)/' => array(
'' => 'DrydockLeaseViewController', '' => 'DrydockLeaseViewController',
'release/' => 'DrydockLeaseReleaseController', 'release/' => 'DrydockLeaseReleaseController',
'logs/(?:query/(?P<queryKey>[^/]+)/)?' =>
'DrydockLogListController',
), ),
), ),
'log/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'DrydockLogListController',
),
), ),
); );
} }

View file

@ -250,46 +250,6 @@ abstract class DrydockBlueprintImplementation extends Phobject {
/* -( Logging )------------------------------------------------------------ */ /* -( 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() { public static function getAllBlueprintImplementations() {
return id(new PhutilClassMapQuery()) return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__) ->setAncestorClass(__CLASS__)

View file

@ -56,11 +56,19 @@ final class DrydockBlueprintViewController extends DrydockBlueprintController {
new DrydockBlueprintTransactionQuery()); new DrydockBlueprintTransactionQuery());
$timeline->setShouldTerminate(true); $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( return $this->buildApplicationPage(
array( array(
$crumbs, $crumbs,
$object_box, $object_box,
$resource_box, $resource_box,
$log_box,
$timeline, $timeline,
), ),
array( array(

View file

@ -85,4 +85,31 @@ abstract class DrydockController extends PhabricatorController {
->addRawContent($table); ->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);
}
} }

View file

@ -15,7 +15,8 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
return new Aphront404Response(); 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()); $title = pht('Lease %d', $lease->getID());
@ -29,20 +30,12 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
$actions = $this->buildActionListView($lease); $actions = $this->buildActionListView($lease);
$properties = $this->buildPropertyListView($lease, $actions); $properties = $this->buildPropertyListView($lease, $actions);
$pager = new PHUIPagerView(); $log_query = id(new DrydockLogQuery())
$pager->setURI(new PhutilURI($lease_uri), 'offset'); ->withLeasePHIDs(array($lease->getPHID()));
$pager->setOffset($request->getInt('offset'));
$logs = id(new DrydockLogQuery()) $log_box = $this->buildLogBox(
->setViewer($viewer) $log_query,
->withLeaseIDs(array($lease->getID())) $this->getApplicationURI("lease/{$id}/logs/query/all/"));
->executeWithOffsetPager($pager);
$log_table = id(new DrydockLogListView())
->setUser($viewer)
->setLogs($logs)
->render();
$log_table->appendChild($pager);
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, $lease_uri); $crumbs->addTextCrumb($title, $lease_uri);
@ -56,10 +49,6 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
->addPropertyList($locks, pht('Slot Locks')) ->addPropertyList($locks, pht('Slot Locks'))
->addPropertyList($commands, pht('Commands')); ->addPropertyList($commands, pht('Commands'));
$log_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Lease Logs'))
->setTable($log_table);
return $this->buildApplicationPage( return $this->buildApplicationPage(
array( array(
$crumbs, $crumbs,

View file

@ -3,13 +3,60 @@
abstract class DrydockLogController abstract class DrydockLogController
extends DrydockController { 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() { public function buildSideNavView() {
$nav = new AphrontSideNavFilterView(); $nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
id(new DrydockLogSearchEngine()) $engine = id(new DrydockLogSearchEngine())
->setViewer($this->getRequest()->getUser()) ->setViewer($this->getRequest()->getUser());
->addNavigationItems($nav->getMenu());
$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); $nav->selectFilter(null);
@ -18,9 +65,54 @@ abstract class DrydockLogController
protected function buildApplicationCrumbs() { protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs(); $crumbs = parent::buildApplicationCrumbs();
$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( $crumbs->addTextCrumb(
pht('Logs'), pht('Logs'),
$this->getApplicationURI('log/')); $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; return $crumbs;
} }

View file

@ -8,11 +8,53 @@ final class DrydockLogListController extends DrydockLogController {
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer(); $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()) $controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($querykey) ->setQueryKey($query_key)
->setSearchEngine(new DrydockLogSearchEngine()) ->setSearchEngine($engine)
->setNavigation($this->buildSideNavView()); ->setNavigation($this->buildSideNavView());
return $this->delegateToController($controller); return $this->delegateToController($controller);

View file

@ -29,23 +29,15 @@ final class DrydockResourceViewController extends DrydockResourceController {
$actions = $this->buildActionListView($resource); $actions = $this->buildActionListView($resource);
$properties = $this->buildPropertyListView($resource, $actions); $properties = $this->buildPropertyListView($resource, $actions);
$resource_uri = 'resource/'.$resource->getID().'/'; $id = $resource->getID();
$resource_uri = $this->getApplicationURI($resource_uri); $resource_uri = $this->getApplicationURI("resource/{$id}/");
$pager = new PHUIPagerView(); $log_query = id(new DrydockLogQuery())
$pager->setURI(new PhutilURI($resource_uri), 'offset'); ->withResourcePHIDs(array($resource->getPHID()));
$pager->setOffset($request->getInt('offset'));
$logs = id(new DrydockLogQuery()) $log_box = $this->buildLogBox(
->setViewer($viewer) $log_query,
->withResourceIDs(array($resource->getID())) $this->getApplicationURI("resource/{$id}/logs/query/all/"));
->executeWithOffsetPager($pager);
$log_table = id(new DrydockLogListView())
->setUser($viewer)
->setLogs($logs)
->render();
$log_table->appendChild($pager);
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Resource %d', $resource->getID())); $crumbs->addTextCrumb(pht('Resource %d', $resource->getID()));
@ -61,10 +53,6 @@ final class DrydockResourceViewController extends DrydockResourceController {
$lease_box = $this->buildLeaseBox($resource); $lease_box = $this->buildLeaseBox($resource);
$log_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Resource Logs'))
->setTable($log_table);
return $this->buildApplicationPage( return $this->buildApplicationPage(
array( array(
$crumbs, $crumbs,

View file

@ -2,112 +2,125 @@
final class DrydockLogQuery extends DrydockQuery { final class DrydockLogQuery extends DrydockQuery {
private $resourceIDs; private $blueprintPHIDs;
private $leaseIDs; private $resourcePHIDs;
private $leasePHIDs;
public function withResourceIDs(array $ids) { public function withBlueprintPHIDs(array $phids) {
$this->resourceIDs = $ids; $this->blueprintPHIDs = $phids;
return $this; return $this;
} }
public function withLeaseIDs(array $ids) { public function withResourcePHIDs(array $phids) {
$this->leaseIDs = $ids; $this->resourcePHIDs = $phids;
return $this; return $this;
} }
public function withLeasePHIDs(array $phids) {
$this->leasePHIDs = $phids;
return $this;
}
public function newResultObject() {
return new DrydockLog();
}
protected function loadPage() { protected function loadPage() {
$table = new DrydockLog(); return $this->loadStandardPage($this->newResultObject());
$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);
} }
protected function willFilterPage(array $logs) { protected function didFilterPage(array $logs) {
$resource_ids = array_filter(mpull($logs, 'getResourceID')); $blueprint_phids = array_filter(mpull($logs, 'getBlueprintPHID'));
if ($resource_ids) { 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()) $resources = id(new DrydockResourceQuery())
->setParentQuery($this) ->setParentQuery($this)
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->withIDs(array_unique($resource_ids)) ->withPHIDs($resource_phids)
->execute(); ->execute();
$resources = mpull($resources, null, 'getPHID');
} else { } else {
$resources = array(); $resources = array();
} }
foreach ($logs as $key => $log) { foreach ($logs as $key => $log) {
$resource = null; $resource = null;
if ($log->getResourceID()) { $resource_phid = $log->getResourcePHID();
$resource = idx($resources, $log->getResourceID()); if ($resource_phid) {
if (!$resource) { $resource = idx($resources, $resource_phid);
unset($logs[$key]);
continue;
}
} }
$log->attachResource($resource); $log->attachResource($resource);
} }
$lease_ids = array_filter(mpull($logs, 'getLeaseID')); $lease_phids = array_filter(mpull($logs, 'getLeasePHID'));
if ($lease_ids) { if ($lease_phids) {
$leases = id(new DrydockLeaseQuery()) $leases = id(new DrydockLeaseQuery())
->setParentQuery($this) ->setParentQuery($this)
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
->withIDs(array_unique($lease_ids)) ->withPHIDs($lease_phids)
->execute(); ->execute();
$leases = mpull($leases, null, 'getPHID');
} else { } else {
$leases = array(); $leases = array();
} }
foreach ($logs as $key => $log) { foreach ($logs as $key => $log) {
$lease = null; $lease = null;
if ($log->getLeaseID()) { $lease_phid = $log->getLeasePHID();
$lease = idx($leases, $log->getLeaseID()); if ($lease_phid) {
if (!$lease) { $lease = idx($leases, $lease_phid);
unset($logs[$key]);
continue;
}
} }
$log->attachLease($lease); $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; return $logs;
} }
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = array(); $where = parent::buildWhereClauseParts($conn);
if ($this->resourceIDs !== null) { if ($this->blueprintPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'resourceID IN (%Ld)', 'blueprintPHID IN (%Ls)',
$this->resourceIDs); $this->blueprintPHIDs);
} }
if ($this->leaseIDs !== null) { if ($this->resourcePHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn,
'leaseID IN (%Ld)', 'resourcePHID IN (%Ls)',
$this->leaseIDs); $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;
} }
} }

View file

@ -2,6 +2,43 @@
final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine { final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine {
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 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;
}
public function getResultTypeDescription() { public function getResultTypeDescription() {
return pht('Drydock Logs'); return pht('Drydock Logs');
} }
@ -10,75 +47,59 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine {
return 'PhabricatorDrydockApplication'; return 'PhabricatorDrydockApplication';
} }
public function buildSavedQueryFromRequest(AphrontRequest $request) { public function newQuery() {
$query = new PhabricatorSavedQuery();
$query->setParameter(
'resourcePHIDs',
$this->readListFromRequest($request, 'resources'));
$query->setParameter(
'leasePHIDs',
$this->readListFromRequest($request, 'leases'));
return $query;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$resource_phids = $saved->getParameter('resourcePHIDs', array());
$lease_phids = $saved->getParameter('leasePHIDs', array());
// TODO: Change logs to use PHIDs instead of IDs.
$resource_ids = array();
$lease_ids = array();
if ($resource_phids) {
$resource_ids = id(new DrydockResourceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($resource_phids)
->execute();
$resource_ids = mpull($resource_ids, 'getID');
}
if ($lease_phids) {
$lease_ids = id(new DrydockLeaseQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($lease_phids)
->execute();
$lease_ids = mpull($lease_ids, 'getID');
}
$query = new DrydockLogQuery(); $query = new DrydockLogQuery();
if ($resource_ids) {
$query->withResourceIDs($resource_ids); $blueprint = $this->getBlueprint();
if ($blueprint) {
$query->withBlueprintPHIDs(array($blueprint->getPHID()));
} }
if ($lease_ids) {
$query->withLeaseIDs($lease_ids); $resource = $this->getResource();
if ($resource) {
$query->withResourcePHIDs(array($resource->getPHID()));
}
$lease = $this->getLease();
if ($lease) {
$query->withLeasePHIDs(array($lease->getPHID()));
} }
return $query; return $query;
} }
public function buildSearchForm( protected function buildQueryFromParameters(array $map) {
AphrontFormView $form, $query = $this->newQuery();
PhabricatorSavedQuery $saved) {
$form return $query;
->appendControl( }
id(new AphrontFormTokenizerControl())
->setDatasource(new DrydockResourceDatasource()) protected function buildCustomSearchFields() {
->setName('resources') return array();
->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())));
} }
protected function getURI($path) { 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() { protected function getBuiltinQueryNames() {

View file

@ -347,6 +347,9 @@ final class DrydockLease extends DrydockDAO
$viewer = PhabricatorUser::getOmnipotentUser(); $viewer = PhabricatorUser::getOmnipotentUser();
$need_update = false; $need_update = false;
// TODO: This is just a placeholder to get some data in the table.
$this->logEvent('activated');
$commands = id(new DrydockCommandQuery()) $commands = id(new DrydockCommandQuery())
->setViewer($viewer) ->setViewer($viewer)
->withTargetPHIDs(array($this->getPHID())) ->withTargetPHIDs(array($this->getPHID()))
@ -371,6 +374,24 @@ final class DrydockLease extends DrydockDAO
} }
} }
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 )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -3,28 +3,38 @@
final class DrydockLog extends DrydockDAO final class DrydockLog extends DrydockDAO
implements PhabricatorPolicyInterface { implements PhabricatorPolicyInterface {
protected $resourceID; protected $blueprintPHID;
protected $leaseID; protected $resourcePHID;
protected $leasePHID;
protected $epoch; protected $epoch;
protected $message; protected $type;
protected $data = array();
private $blueprint = self::ATTACHABLE;
private $resource = self::ATTACHABLE; private $resource = self::ATTACHABLE;
private $lease = self::ATTACHABLE; private $lease = self::ATTACHABLE;
protected function getConfiguration() { protected function getConfiguration() {
return array( return array(
self::CONFIG_TIMESTAMPS => false, self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'data' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array( self::CONFIG_COLUMN_SCHEMA => array(
'resourceID' => 'id?', 'blueprintPHID' => 'phid?',
'leaseID' => 'id?', 'resourcePHID' => 'phid?',
'message' => 'text', 'leasePHID' => 'phid?',
'type' => 'text64',
), ),
self::CONFIG_KEY_SCHEMA => array( self::CONFIG_KEY_SCHEMA => array(
'resourceID' => array( 'key_blueprint' => array(
'columns' => array('resourceID', 'epoch'), 'columns' => array('blueprintPHID', 'type'),
), ),
'leaseID' => array( 'key_resource' => array(
'columns' => array('leaseID', 'epoch'), 'columns' => array('resourcePHID', 'type'),
),
'key_lease' => array(
'columns' => array('leasePHID', 'type'),
), ),
'epoch' => array( 'epoch' => array(
'columns' => array('epoch'), 'columns' => array('epoch'),
@ -33,6 +43,15 @@ final class DrydockLog extends DrydockDAO
) + parent::getConfiguration(); ) + 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) { public function attachResource(DrydockResource $resource = null) {
$this->resource = $resource; $this->resource = $resource;
return $this; return $this;
@ -51,6 +70,22 @@ final class DrydockLog extends DrydockDAO
return $this->assertAttached($this->lease); 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 )----------------------------------------- */ /* -( PhabricatorPolicyInterface )----------------------------------------- */
@ -62,21 +97,19 @@ final class DrydockLog extends DrydockDAO
} }
public function getPolicy($capability) { public function getPolicy($capability) {
if ($this->getResource()) { // NOTE: We let you see that logs exist no matter what, but don't actually
return $this->getResource()->getPolicy($capability); // show you log content unless you can see all of the associated objects.
} return PhabricatorPolicies::getMostOpenPolicy();
return $this->getLease()->getPolicy($capability);
} }
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->getResource()) { return false;
return $this->getResource()->hasAutomaticCapability($capability, $viewer);
}
return $this->getLease()->hasAutomaticCapability($capability, $viewer);
} }
public function describeAutomaticCapability($capability) { 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.');
} }
} }

View file

@ -18,28 +18,45 @@ final class DrydockLogListView extends AphrontView {
$rows = array(); $rows = array();
foreach ($logs as $log) { foreach ($logs as $log) {
$resource_uri = '/drydock/resource/'.$log->getResourceID().'/'; $blueprint_phid = $log->getBlueprintPHID();
$lease_uri = '/drydock/lease/'.$log->getLeaseID().'/'; if ($blueprint_phid) {
$blueprint = $viewer->renderHandle($blueprint_phid);
} else {
$blueprint = null;
}
$resource_name = $log->getResourceID(); $resource_phid = $log->getResourcePHID();
if ($log->getResourceID() !== null) { if ($resource_phid) {
$resource_name = $log->getResource()->getName(); $resource = $viewer->renderHandle($resource_phid);
} else {
$resource = null;
}
$lease_phid = $log->getLeasePHID();
if ($lease_phid) {
$lease = $viewer->renderHandle($lease_phid);
} else {
$lease = null;
}
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( $rows[] = array(
phutil_tag( $blueprint,
'a', $resource,
array( $lease,
'href' => $resource_uri, $type,
), $data,
$resource_name),
phutil_tag(
'a',
array(
'href' => $lease_uri,
),
$log->getLeaseID()),
$log->getMessage(),
phabricator_datetime($log->getEpoch(), $viewer), phabricator_datetime($log->getEpoch(), $viewer),
); );
} }
@ -48,20 +65,17 @@ final class DrydockLogListView extends AphrontView {
$table->setDeviceReadyTable(true); $table->setDeviceReadyTable(true);
$table->setHeaders( $table->setHeaders(
array( array(
pht('Blueprint'),
pht('Resource'), pht('Resource'),
pht('Lease'), pht('Lease'),
pht('Message'), pht('Type'),
pht('Data'),
pht('Date'), pht('Date'),
)); ));
$table->setShortHeaders(
array(
pht('R'),
pht('L'),
pht('Message'),
'',
));
$table->setColumnClasses( $table->setColumnClasses(
array( array(
'',
'',
'', '',
'', '',
'wide', 'wide',