Page MenuHomePhabricator

D8604.diff
No OneTemporary

D8604.diff

diff --git a/resources/sql/autopatches/20140323.harbor.2.message.sql b/resources/sql/autopatches/20140323.harbor.2.message.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20140323.harbor.2.message.sql
@@ -0,0 +1,10 @@
+CREATE TABLE {$NAMESPACE}_harbormaster.harbormaster_buildmessage (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ buildTargetPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ type VARCHAR(16) NOT NULL,
+ isConsumed BOOL NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ KEY `key_buildtarget` (buildTargetPHID)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -189,6 +189,8 @@
'ConduitAPI_flag_delete_Method' => 'applications/flag/conduit/ConduitAPI_flag_delete_Method.php',
'ConduitAPI_flag_edit_Method' => 'applications/flag/conduit/ConduitAPI_flag_edit_Method.php',
'ConduitAPI_flag_query_Method' => 'applications/flag/conduit/ConduitAPI_flag_query_Method.php',
+ 'ConduitAPI_harbormaster_Method' => 'applications/harbormaster/conduit/ConduitAPI_harbormaster_Method.php',
+ 'ConduitAPI_harbormaster_sendmessage_Method' => 'applications/harbormaster/conduit/ConduitAPI_harbormaster_sendmessage_Method.php',
'ConduitAPI_macro_Method' => 'applications/macro/conduit/ConduitAPI_macro_Method.php',
'ConduitAPI_macro_creatememe_Method' => 'applications/macro/conduit/ConduitAPI_macro_creatememe_Method.php',
'ConduitAPI_macro_query_Method' => 'applications/macro/conduit/ConduitAPI_macro_query_Method.php',
@@ -701,6 +703,8 @@
'HarbormasterBuildItemQuery' => 'applications/harbormaster/query/HarbormasterBuildItemQuery.php',
'HarbormasterBuildLog' => 'applications/harbormaster/storage/build/HarbormasterBuildLog.php',
'HarbormasterBuildLogQuery' => 'applications/harbormaster/query/HarbormasterBuildLogQuery.php',
+ 'HarbormasterBuildMessage' => 'applications/harbormaster/storage/HarbormasterBuildMessage.php',
+ 'HarbormasterBuildMessageQuery' => 'applications/harbormaster/query/HarbormasterBuildMessageQuery.php',
'HarbormasterBuildPlan' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php',
'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php',
'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
@@ -2743,6 +2747,8 @@
'ConduitAPI_flag_delete_Method' => 'ConduitAPI_flag_Method',
'ConduitAPI_flag_edit_Method' => 'ConduitAPI_flag_Method',
'ConduitAPI_flag_query_Method' => 'ConduitAPI_flag_Method',
+ 'ConduitAPI_harbormaster_Method' => 'ConduitAPIMethod',
+ 'ConduitAPI_harbormaster_sendmessage_Method' => 'ConduitAPI_harbormaster_Method',
'ConduitAPI_macro_Method' => 'ConduitAPIMethod',
'ConduitAPI_macro_creatememe_Method' => 'ConduitAPI_macro_Method',
'ConduitAPI_macro_query_Method' => 'ConduitAPI_macro_Method',
@@ -3297,6 +3303,12 @@
1 => 'PhabricatorPolicyInterface',
),
'HarbormasterBuildLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'HarbormasterBuildMessage' =>
+ array(
+ 0 => 'HarbormasterDAO',
+ 1 => 'PhabricatorPolicyInterface',
+ ),
+ 'HarbormasterBuildMessageQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildPlan' =>
array(
0 => 'HarbormasterDAO',
diff --git a/src/applications/harbormaster/conduit/ConduitAPI_harbormaster_Method.php b/src/applications/harbormaster/conduit/ConduitAPI_harbormaster_Method.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/conduit/ConduitAPI_harbormaster_Method.php
@@ -0,0 +1,18 @@
+<?php
+
+abstract class ConduitAPI_harbormaster_Method extends ConduitAPIMethod {
+
+ public function getApplication() {
+ return PhabricatorApplication::getByClass(
+ 'PhabricatorApplicationHarbormaster');
+ }
+
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_UNSTABLE;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht('All Harbormaster APIs are new and subject to change.');
+ }
+
+}
diff --git a/src/applications/harbormaster/conduit/ConduitAPI_harbormaster_sendmessage_Method.php b/src/applications/harbormaster/conduit/ConduitAPI_harbormaster_sendmessage_Method.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/conduit/ConduitAPI_harbormaster_sendmessage_Method.php
@@ -0,0 +1,49 @@
+<?php
+
+final class ConduitAPI_harbormaster_sendmessage_Method
+ extends ConduitAPI_harbormaster_Method {
+
+ public function getMethodDescription() {
+ return pht(
+ 'Send a message to a build target, notifying it of results in an '.
+ 'external system.');
+ }
+
+ public function defineParamTypes() {
+ return array(
+ 'buildTargetPHID' => 'phid',
+ 'type' => 'enum<pass, fail>',
+ );
+ }
+
+ public function defineReturnType() {
+ return 'void';
+ }
+
+ public function defineErrorTypes() {
+ return array();
+ }
+
+ protected function execute(ConduitAPIRequest $request) {
+ $viewer = $request->getUser();
+
+ $build_target_phid = $request->getValue('buildTargetPHID');
+ $message_type = $request->getValue('type');
+
+ $build_target = id(new HarbormasterBuildTargetQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($build_target_phid))
+ ->executeOne();
+ if (!$build_target) {
+ throw new Exception(pht('No such build target!'));
+ }
+
+ $message = HarbormasterBuildMessage::initializeNewMessage($viewer)
+ ->setBuildTargetPHID($build_target->getPHID())
+ ->setType($message_type)
+ ->save();
+
+ return null;
+ }
+
+}
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
--- a/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
+++ b/src/applications/harbormaster/controller/HarbormasterBuildViewController.php
@@ -55,6 +55,17 @@
->withBuildPHIDs(array($build->getPHID()))
->execute();
+
+ if ($build_targets) {
+ $messages = id(new HarbormasterBuildMessageQuery())
+ ->setViewer($viewer)
+ ->withBuildTargetPHIDs(mpull($build_targets, 'getPHID'))
+ ->execute();
+ $messages = mgroup($messages, 'getBuildTargetPHID');
+ } else {
+ $messages = array();
+ }
+
$targets = array();
foreach ($build_targets as $build_target) {
$header = id(new PHUIHeaderView())
@@ -85,6 +96,9 @@
->setHeader($header)
->addPropertyList($properties);
+ $build_messages = idx($messages, $build_target->getPHID(), array());
+ $targets[] = $this->buildMessages($build_messages);
+
$targets[] = $this->buildArtifacts($build_target);
$targets[] = $this->buildLog($build, $build_target);
}
@@ -316,4 +330,55 @@
}
}
+ private function buildMessages(array $messages) {
+ $viewer = $this->getRequest()->getUser();
+
+ if ($messages) {
+ $handles = id(new PhabricatorHandleQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(mpull($messages, 'getAuthorPHID'))
+ ->execute();
+ } else {
+ $handles = array();
+ }
+
+ $rows = array();
+ foreach ($messages as $message) {
+ $rows[] = array(
+ $message->getID(),
+ $handles[$message->getAuthorPHID()]->renderLink(),
+ $message->getType(),
+ $message->getIsConsumed() ? pht('Consumed') : null,
+ phabricator_datetime($message->getDateCreated(), $viewer),
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setNoDataString(pht('No messages for this build target.'));
+ $table->setHeaders(
+ array(
+ pht('ID'),
+ pht('From'),
+ pht('Type'),
+ pht('Consumed'),
+ pht('Received'),
+ ));
+ $table->setColumnClasses(
+ array(
+ '',
+ '',
+ 'wide',
+ '',
+ 'date',
+ ));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Build Target Messages'))
+ ->appendChild($table);
+
+ return $box;
+ }
+
+
+
}
diff --git a/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/query/HarbormasterBuildMessageQuery.php
@@ -0,0 +1,98 @@
+<?php
+
+final class HarbormasterBuildMessageQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $buildTargetPHIDs;
+ private $consumed;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withBuildTargetPHIDs(array $phids) {
+ $this->buildTargetPHIDs = $phids;
+ return $this;
+ }
+
+ public function withConsumed($consumed) {
+ $this->consumed = $consumed;
+ return $this;
+ }
+
+ protected function loadPage() {
+ $table = new HarbormasterBuildMessage();
+ $conn_r = $table->establishConnection('r');
+
+ $data = queryfx_all(
+ $conn_r,
+ 'SELECT * FROM %T %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 $page) {
+ $build_target_phids = array_filter(mpull($page, 'getBuildTargetPHID'));
+ if ($build_target_phids) {
+ $build_targets = id(new PhabricatorObjectQuery())
+ ->setViewer($this->getViewer())
+ ->withPHIDs($build_target_phids)
+ ->setParentQuery($this)
+ ->execute();
+ $build_targets = mpull($build_targets, null, 'getPHID');
+ } else {
+ $build_targets = array();
+ }
+
+ foreach ($page as $key => $message) {
+ $build_target_phid = $message->getBuildTargetPHID();
+ if (empty($build_targets[$build_target_phid])) {
+ unset($page[$key]);
+ continue;
+ }
+ $message->attachBuildTarget($build_targets[$build_target_phid]);
+ }
+
+ return $page;
+ }
+
+ private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
+ $where = array();
+
+ if ($this->ids) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->buildTargetPHIDs) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'buildTargetPHID IN (%Ls)',
+ $this->buildTargetPHIDs);
+ }
+
+ if ($this->consumed !== null) {
+ $where[] = qsprintf(
+ $conn_r,
+ 'isConsumed = %d',
+ (int)$this->isConsumed);
+ }
+
+ $where[] = $this->buildPagingClause($conn_r);
+
+ return $this->formatWhereClause($where);
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorApplicationHarbormaster';
+ }
+
+}
diff --git a/src/applications/harbormaster/storage/HarbormasterBuildMessage.php b/src/applications/harbormaster/storage/HarbormasterBuildMessage.php
new file mode 100644
--- /dev/null
+++ b/src/applications/harbormaster/storage/HarbormasterBuildMessage.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * A message sent to an executing build target by an external system. We
+ * capture these messages and process them asynchronously to avoid race
+ * conditions where we receive a message before a build plan is ready to
+ * accept it.
+ */
+final class HarbormasterBuildMessage extends HarbormasterDAO
+ implements PhabricatorPolicyInterface {
+
+ protected $authorPHID;
+ protected $buildTargetPHID;
+ protected $type;
+ protected $isConsumed;
+
+ private $buildTarget = self::ATTACHABLE;
+
+ public static function initializeNewMessage(PhabricatorUser $actor) {
+ return id(new HarbormasterBuildMessage())
+ ->setAuthorPHID($actor->getPHID())
+ ->setIsConsumed(0);
+ }
+
+ public function getBuildTarget() {
+ return $this->assertAttached($this->buildTarget);
+ }
+
+ public function attachBuildTarget(HarbormasterBuildTarget $target) {
+ $this->buildTarget = $target;
+ return $this;
+ }
+
+
+/* -( 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('Build messages have the same policies as their targets.');
+ }
+
+}
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuild.php b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuild.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuild.php
@@ -168,7 +168,8 @@
'repository.vcs' => null,
'repository.uri' => null,
'step.timestamp' => null,
- 'build.id' => null);
+ 'build.id' => null,
+ );
$buildable = $this->getBuildable();
$object = $buildable->getBuildableObject();
@@ -210,7 +211,9 @@
'repository.uri' =>
pht('The URI to clone or checkout the repository from.'),
'step.timestamp' => pht('The current UNIX timestamp.'),
- 'build.id' => pht('The ID of the current build.'));
+ 'build.id' => pht('The ID of the current build.'),
+ 'target.phid' => pht('The PHID of the current build target.'),
+ );
}
public function isComplete() {
diff --git a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
--- a/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
+++ b/src/applications/harbormaster/storage/build/HarbormasterBuildTarget.php
@@ -73,6 +73,10 @@
return $this;
}
+ public function getVariables() {
+ return parent::getVariables() + $this->getBuildTargetVariables();
+ }
+
public function getVariable($key, $default = null) {
return idx($this->variables, $key, $default);
}
@@ -93,6 +97,12 @@
return $this->implementation;
}
+ private function getBuildTargetVariables() {
+ return array(
+ 'target.phid' => $this->getPHID(),
+ );
+ }
+
/* -( Status )------------------------------------------------------------- */

File Metadata

Mime Type
text/plain
Expires
Thu, Mar 27, 10:11 AM (3 w, 13 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7704950
Default Alt Text
D8604.diff (14 KB)

Event Timeline