1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-26 05:29:06 +01:00
phorge-phorge/src/applications/harbormaster/controller/HarbormasterBuildViewController.php

378 lines
10 KiB
PHP
Raw Normal View History

<?php
final class HarbormasterBuildViewController
extends HarbormasterController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $this->id;
$build = id(new HarbormasterBuildQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$build) {
return new Aphront404Response();
}
$title = pht('Build %d', $id);
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($build);
if ($build->isRestarting()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Restarting'));
} else if ($build->isStopping()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Stopping'));
} else if ($build->isResuming()) {
$header->setStatus('fa-exclamation-triangle', 'red', pht('Resuming'));
}
$box = id(new PHUIObjectBoxView())
->setHeader($header);
$actions = $this->buildActionList($build);
$this->buildPropertyLists($box, $build, $actions);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
$build->getBuildable()->getMonogram(),
'/'.$build->getBuildable()->getMonogram());
$crumbs->addTextCrumb($title);
$build_targets = id(new HarbormasterBuildTargetQuery())
->setViewer($viewer)
->withBuildPHIDs(array($build->getPHID()))
->execute();
Allow external systems to send messages to build targets Summary: Ref T1049. Allows external systems to send a message to a build target. The primary intended use case is: - You make an HTTP request to Jenkins. - The build goes into a "waiting" state. - Later, Jenkins calls `harbormaster.sendmessage` to report that the target passed or failed. - The build continues as appropriate. This is deceptively complicated because: - There are a lot of race concerns. We might get a message back from an external system before it even responds to the request we made. We want to make sure we process these messages no matter when we receive them. - These messages need to be sent to a build target (vs a build or buildable) because we'll get into trouble with parallelization later on otherwise (Jenkins is told to do 3 builds; we can't tell which ones failed or what overall state is unless the message are sent to targets). - I initially thought about implementing this as a separate "Wait for a response from an external system" build step. This gets a lot more complicated for users once we do parallelization, though. Particularly, in the case where you've told Jenkins to do 3 builds, the three "wait" steps need to know which target they're waiting for (and jenkins needs to know some unique identifier for each target). So this pretty much boils down to a more complicated, more error-prone version of using target PHIDs. This makes the already-muddy Build UI a bit worse, but it needs a general clarity pass anyway (it's showing way too much uninteresting data, and should show a better summary of results instead). Test Plan: - This doesn't really do anything interesting yet. - Used Conduit to send messages to build plans. - Viewed the messages on the build screen. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D8604
2014-03-25 16:11:28 -07:00
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())
->setHeader(pht(
'Build Target %d (%s)',
$build_target->getID(),
$build_target->getName()))
->setUser($viewer);
$properties = new PHUIPropertyListView();
$details = $build_target->getDetails();
if ($details) {
$properties->addSectionHeader(pht('Configuration Details'));
foreach ($details as $key => $value) {
$properties->addProperty($key, $value);
}
}
$variables = $build_target->getVariables();
if ($variables) {
$properties->addSectionHeader(pht('Variables'));
foreach ($variables as $key => $value) {
$properties->addProperty($key, $value);
}
}
$targets[] = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
Allow external systems to send messages to build targets Summary: Ref T1049. Allows external systems to send a message to a build target. The primary intended use case is: - You make an HTTP request to Jenkins. - The build goes into a "waiting" state. - Later, Jenkins calls `harbormaster.sendmessage` to report that the target passed or failed. - The build continues as appropriate. This is deceptively complicated because: - There are a lot of race concerns. We might get a message back from an external system before it even responds to the request we made. We want to make sure we process these messages no matter when we receive them. - These messages need to be sent to a build target (vs a build or buildable) because we'll get into trouble with parallelization later on otherwise (Jenkins is told to do 3 builds; we can't tell which ones failed or what overall state is unless the message are sent to targets). - I initially thought about implementing this as a separate "Wait for a response from an external system" build step. This gets a lot more complicated for users once we do parallelization, though. Particularly, in the case where you've told Jenkins to do 3 builds, the three "wait" steps need to know which target they're waiting for (and jenkins needs to know some unique identifier for each target). So this pretty much boils down to a more complicated, more error-prone version of using target PHIDs. This makes the already-muddy Build UI a bit worse, but it needs a general clarity pass anyway (it's showing way too much uninteresting data, and should show a better summary of results instead). Test Plan: - This doesn't really do anything interesting yet. - Used Conduit to send messages to build plans. - Viewed the messages on the build screen. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D8604
2014-03-25 16:11:28 -07:00
$build_messages = idx($messages, $build_target->getPHID(), array());
if ($build_messages) {
$targets[] = $this->buildMessages($build_messages);
}
Allow external systems to send messages to build targets Summary: Ref T1049. Allows external systems to send a message to a build target. The primary intended use case is: - You make an HTTP request to Jenkins. - The build goes into a "waiting" state. - Later, Jenkins calls `harbormaster.sendmessage` to report that the target passed or failed. - The build continues as appropriate. This is deceptively complicated because: - There are a lot of race concerns. We might get a message back from an external system before it even responds to the request we made. We want to make sure we process these messages no matter when we receive them. - These messages need to be sent to a build target (vs a build or buildable) because we'll get into trouble with parallelization later on otherwise (Jenkins is told to do 3 builds; we can't tell which ones failed or what overall state is unless the message are sent to targets). - I initially thought about implementing this as a separate "Wait for a response from an external system" build step. This gets a lot more complicated for users once we do parallelization, though. Particularly, in the case where you've told Jenkins to do 3 builds, the three "wait" steps need to know which target they're waiting for (and jenkins needs to know some unique identifier for each target). So this pretty much boils down to a more complicated, more error-prone version of using target PHIDs. This makes the already-muddy Build UI a bit worse, but it needs a general clarity pass anyway (it's showing way too much uninteresting data, and should show a better summary of results instead). Test Plan: - This doesn't really do anything interesting yet. - Used Conduit to send messages to build plans. - Viewed the messages on the build screen. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D8604
2014-03-25 16:11:28 -07:00
$targets[] = $this->buildArtifacts($build_target);
$targets[] = $this->buildLog($build, $build_target);
}
$xactions = id(new HarbormasterBuildTransactionQuery())
->setViewer($viewer)
->withObjectPHIDs(array($build->getPHID()))
->execute();
$timeline = id(new PhabricatorApplicationTransactionView())
->setUser($viewer)
->setObjectPHID($build->getPHID())
->setTransactions($xactions);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$targets,
$timeline,
),
array(
'title' => $title,
));
}
private function buildArtifacts(HarbormasterBuildTarget $build_target) {
$request = $this->getRequest();
$viewer = $request->getUser();
$artifacts = id(new HarbormasterBuildArtifactQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
if (count($artifacts) === 0) {
return null;
}
$list = new PHUIObjectItemListView();
foreach ($artifacts as $artifact) {
$list->addItem($artifact->getObjectItemView($viewer));
}
$header = id(new PHUIHeaderView())
->setHeader(pht('Build Artifacts'))
->setUser($viewer);
$box = id(new PHUIObjectBoxView())
->setHeader($header);
return array($box, $list);
}
private function buildLog(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$request = $this->getRequest();
$viewer = $request->getUser();
$limit = $request->getInt('l', 25);
$logs = id(new HarbormasterBuildLogQuery())
->setViewer($viewer)
->withBuildTargetPHIDs(array($build_target->getPHID()))
->execute();
$log_boxes = array();
foreach ($logs as $log) {
$start = 1;
$lines = preg_split("/\r\n|\r|\n/", $log->getLogText());
if ($limit !== 0) {
$start = count($lines) - $limit;
if ($start >= 1) {
$lines = array_slice($lines, -$limit, $limit);
} else {
$start = 1;
}
}
$log_view = new ShellLogView();
$log_view->setLines($lines);
$log_view->setStart($start);
$header = id(new PHUIHeaderView())
->setHeader(pht(
'Build Log %d (%s - %s)',
$log->getID(),
$log->getLogSource(),
$log->getLogType()))
->setSubheader($this->createLogHeader($build, $log))
->setUser($viewer);
$log_boxes[] = id(new PHUIObjectBoxView())
->setHeader($header)
->setForm($log_view);
}
return $log_boxes;
}
private function createLogHeader($build, $log) {
$request = $this->getRequest();
$limit = $request->getInt('l', 25);
$lines_25 = $this->getApplicationURI('/build/'.$build->getID().'/?l=25');
$lines_50 = $this->getApplicationURI('/build/'.$build->getID().'/?l=50');
$lines_100 =
$this->getApplicationURI('/build/'.$build->getID().'/?l=100');
$lines_0 = $this->getApplicationURI('/build/'.$build->getID().'/?l=0');
$link_25 = phutil_tag('a', array('href' => $lines_25), pht('25'));
$link_50 = phutil_tag('a', array('href' => $lines_50), pht('50'));
$link_100 = phutil_tag('a', array('href' => $lines_100), pht('100'));
$link_0 = phutil_tag('a', array('href' => $lines_0), pht('Unlimited'));
if ($limit === 25) {
$link_25 = phutil_tag('strong', array(), $link_25);
} else if ($limit === 50) {
$link_50 = phutil_tag('strong', array(), $link_50);
} else if ($limit === 100) {
$link_100 = phutil_tag('strong', array(), $link_100);
} else if ($limit === 0) {
$link_0 = phutil_tag('strong', array(), $link_0);
}
return phutil_tag(
'span',
array(),
array(
$link_25,
' - ',
$link_50,
' - ',
$link_100,
' - ',
$link_0,
' Lines'));
}
private function buildActionList(HarbormasterBuild $build) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $build->getID();
$list = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($build)
->setObjectURI("/build/{$id}");
$can_restart = $build->canRestartBuild();
$can_stop = $build->canStopBuild();
$can_resume = $build->canResumeBuild();
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Restart Build'))
->setIcon('fa-backward')
->setHref($this->getApplicationURI('/build/restart/'.$id.'/'))
->setDisabled(!$can_restart)
->setWorkflow(true));
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Stop Build'))
->setIcon('fa-stop')
->setHref($this->getApplicationURI('/build/stop/'.$id.'/'))
->setDisabled(!$can_stop)
->setWorkflow(true));
$list->addAction(
id(new PhabricatorActionView())
->setName(pht('Resume Build'))
->setIcon('fa-play')
->setHref($this->getApplicationURI('/build/resume/'.$id.'/'))
->setDisabled(!$can_resume)
->setWorkflow(true));
return $list;
}
private function buildPropertyLists(
PHUIObjectBoxView $box,
HarbormasterBuild $build,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$viewer = $request->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($build)
->setActionList($actions);
$box->addPropertyList($properties);
$properties->addProperty(
pht('Status'),
$this->getStatus($build));
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(array(
$build->getBuildablePHID(),
$build->getBuildPlanPHID()))
->execute();
$properties->addProperty(
pht('Buildable'),
$handles[$build->getBuildablePHID()]->renderLink());
$properties->addProperty(
pht('Build Plan'),
$handles[$build->getBuildPlanPHID()]->renderLink());
}
private function getStatus(HarbormasterBuild $build) {
if ($build->isStopping()) {
return pht('Stopping');
}
return HarbormasterBuild::getBuildStatusName($build->getBuildStatus());
}
Allow external systems to send messages to build targets Summary: Ref T1049. Allows external systems to send a message to a build target. The primary intended use case is: - You make an HTTP request to Jenkins. - The build goes into a "waiting" state. - Later, Jenkins calls `harbormaster.sendmessage` to report that the target passed or failed. - The build continues as appropriate. This is deceptively complicated because: - There are a lot of race concerns. We might get a message back from an external system before it even responds to the request we made. We want to make sure we process these messages no matter when we receive them. - These messages need to be sent to a build target (vs a build or buildable) because we'll get into trouble with parallelization later on otherwise (Jenkins is told to do 3 builds; we can't tell which ones failed or what overall state is unless the message are sent to targets). - I initially thought about implementing this as a separate "Wait for a response from an external system" build step. This gets a lot more complicated for users once we do parallelization, though. Particularly, in the case where you've told Jenkins to do 3 builds, the three "wait" steps need to know which target they're waiting for (and jenkins needs to know some unique identifier for each target). So this pretty much boils down to a more complicated, more error-prone version of using target PHIDs. This makes the already-muddy Build UI a bit worse, but it needs a general clarity pass anyway (it's showing way too much uninteresting data, and should show a better summary of results instead). Test Plan: - This doesn't really do anything interesting yet. - Used Conduit to send messages to build plans. - Viewed the messages on the build screen. Reviewers: btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T1049 Differential Revision: https://secure.phabricator.com/D8604
2014-03-25 16:11:28 -07:00
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;
}
}