Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15439908
D8604.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Referenced Files
None
Subscribers
None
D8604.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8604: Allow external systems to send messages to build targets
Attached
Detach File
Event Timeline
Log In to Comment