Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php b/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php
index 2486712022..a61f0e3535 100644
--- a/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php
+++ b/src/applications/config/controller/PhabricatorConfigClusterRepositoriesController.php
@@ -1,341 +1,412 @@
<?php
final class PhabricatorConfigClusterRepositoriesController
extends PhabricatorConfigController {
public function handleRequest(AphrontRequest $request) {
$nav = $this->buildSideNavView();
$nav->selectFilter('cluster/repositories/');
$title = pht('Cluster Repository Status');
$doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories');
$header = id(new PHUIHeaderView())
->setHeader($title)
->setProfileHeader(true)
->addActionLink(
id(new PHUIButtonView())
->setIcon('fa-book')
->setHref($doc_href)
->setTag('a')
->setText(pht('Documentation')));
$crumbs = $this
->buildApplicationCrumbs($nav)
->addTextCrumb(pht('Repository Servers'))
->setBorder(true);
$repository_status = $this->buildClusterRepositoryStatus();
+ $repository_errors = $this->buildClusterRepositoryErrors();
$content = id(new PhabricatorConfigPageView())
->setHeader($header)
- ->setContent($repository_status);
+ ->setContent(
+ array(
+ $repository_status,
+ $repository_errors,
+ ));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($content)
->addClass('white-background');
}
private function buildClusterRepositoryStatus() {
$viewer = $this->getViewer();
Javelin::initBehavior('phabricator-tooltips');
$all_services = id(new AlmanacServiceQuery())
->setViewer($viewer)
->withServiceTypes(
array(
AlmanacClusterRepositoryServiceType::SERVICETYPE,
))
->needBindings(true)
->needProperties(true)
->execute();
$all_services = mpull($all_services, null, 'getPHID');
$all_repositories = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withTypes(
array(
PhabricatorRepositoryType::REPOSITORY_TYPE_GIT,
))
->execute();
$all_repositories = mpull($all_repositories, null, 'getPHID');
$all_versions = id(new PhabricatorRepositoryWorkingCopyVersion())
->loadAll();
$all_devices = $this->getDevices($all_services, false);
$all_active_devices = $this->getDevices($all_services, true);
$leader_versions = $this->getLeaderVersionsByRepository(
$all_repositories,
$all_versions,
$all_active_devices);
$push_times = $this->loadLeaderPushTimes($leader_versions);
$repository_groups = mgroup($all_repositories, 'getAlmanacServicePHID');
$repository_versions = mgroup($all_versions, 'getRepositoryPHID');
$rows = array();
foreach ($all_services as $service) {
$service_phid = $service->getPHID();
if ($service->getAlmanacPropertyValue('closed')) {
$status_icon = 'fa-folder';
$status_tip = pht('Closed');
} else {
$status_icon = 'fa-folder-open green';
$status_tip = pht('Open');
}
$status_icon = id(new PHUIIconView())
->setIcon($status_icon)
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => $status_tip,
));
$devices = idx($all_devices, $service_phid, array());
$active_devices = idx($all_active_devices, $service_phid, array());
$device_icon = 'fa-server green';
$device_label = pht(
'%s Active',
phutil_count($active_devices));
$device_status = array(
id(new PHUIIconView())->setIcon($device_icon),
' ',
$device_label,
);
$repositories = idx($repository_groups, $service_phid, array());
$repository_status = pht(
'%s',
phutil_count($repositories));
$no_leader = array();
$full_sync = array();
$partial_sync = array();
$no_sync = array();
$lag = array();
// Threshold in seconds before we start complaining that repositories
// are not synchronized when there is only one leader.
$threshold = phutil_units('5 minutes in seconds');
$messages = array();
foreach ($repositories as $repository) {
$repository_phid = $repository->getPHID();
$leader_version = idx($leader_versions, $repository_phid);
if ($leader_version === null) {
$no_leader[] = $repository;
$messages[] = pht(
'Repository %s has an ambiguous leader.',
$viewer->renderHandle($repository_phid)->render());
continue;
}
$versions = idx($repository_versions, $repository_phid, array());
$leaders = 0;
foreach ($versions as $version) {
if ($version->getRepositoryVersion() == $leader_version) {
$leaders++;
}
}
if ($leaders == count($active_devices)) {
$full_sync[] = $repository;
} else {
$push_epoch = idx($push_times, $repository_phid);
if ($push_epoch) {
$duration = (PhabricatorTime::getNow() - $push_epoch);
$lag[] = $duration;
} else {
$duration = null;
}
if ($leaders >= 2 || ($duration && ($duration < $threshold))) {
$partial_sync[] = $repository;
} else {
$no_sync[] = $repository;
if ($push_epoch) {
$messages[] = pht(
'Repository %s has unreplicated changes (for %s).',
$viewer->renderHandle($repository_phid)->render(),
phutil_format_relative_time($duration));
} else {
$messages[] = pht(
'Repository %s has unreplicated changes.',
$viewer->renderHandle($repository_phid)->render());
}
}
}
}
$with_lag = false;
if ($no_leader) {
$replication_icon = 'fa-times red';
$replication_label = pht('Ambiguous Leader');
} else if ($no_sync) {
$replication_icon = 'fa-refresh yellow';
$replication_label = pht('Unsynchronized');
$with_lag = true;
} else if ($partial_sync) {
$replication_icon = 'fa-refresh green';
$replication_label = pht('Partial');
$with_lag = true;
} else if ($full_sync) {
$replication_icon = 'fa-check green';
$replication_label = pht('Synchronized');
} else {
$replication_icon = 'fa-times grey';
$replication_label = pht('No Repositories');
}
if ($with_lag && $lag) {
$lag_status = phutil_format_relative_time(max($lag));
$lag_status = pht(' (%s)', $lag_status);
} else {
$lag_status = null;
}
$replication_status = array(
id(new PHUIIconView())->setIcon($replication_icon),
' ',
$replication_label,
$lag_status,
);
$messages = phutil_implode_html(phutil_tag('br'), $messages);
$rows[] = array(
$status_icon,
$viewer->renderHandle($service->getPHID()),
$device_status,
$repository_status,
$replication_status,
$messages,
);
}
return id(new AphrontTableView($rows))
->setNoDataString(
pht('No repository cluster services are configured.'))
->setHeaders(
array(
null,
pht('Service'),
pht('Devices'),
pht('Repos'),
pht('Sync'),
pht('Messages'),
))
->setColumnClasses(
array(
null,
'pri',
null,
null,
null,
'wide',
));
}
private function getDevices(
array $all_services,
$only_active) {
$devices = array();
foreach ($all_services as $service) {
$map = array();
foreach ($service->getBindings() as $binding) {
if ($only_active && $binding->getIsDisabled()) {
continue;
}
$device = $binding->getDevice();
$device_phid = $device->getPHID();
$map[$device_phid] = $device;
}
$devices[$service->getPHID()] = $map;
}
return $devices;
}
private function getLeaderVersionsByRepository(
array $all_repositories,
array $all_versions,
array $active_devices) {
$version_map = mgroup($all_versions, 'getRepositoryPHID');
$result = array();
foreach ($all_repositories as $repository_phid => $repository) {
$service_phid = $repository->getAlmanacServicePHID();
if (!$service_phid) {
continue;
}
$devices = idx($active_devices, $service_phid);
if (!$devices) {
continue;
}
$versions = idx($version_map, $repository_phid, array());
$versions = mpull($versions, null, 'getDevicePHID');
$versions = array_select_keys($versions, array_keys($devices));
if (!$versions) {
continue;
}
$leader = (int)max(mpull($versions, 'getRepositoryVersion'));
$result[$repository_phid] = $leader;
}
return $result;
}
private function loadLeaderPushTimes(array $leader_versions) {
$viewer = $this->getViewer();
if (!$leader_versions) {
return array();
}
$events = id(new PhabricatorRepositoryPushEventQuery())
->setViewer($viewer)
->withIDs($leader_versions)
->execute();
$events = mpull($events, null, 'getID');
$result = array();
foreach ($leader_versions as $key => $version) {
$event = idx($events, $version);
if (!$event) {
continue;
}
$result[$key] = $event->getEpoch();
}
return $result;
}
+ private function buildClusterRepositoryErrors() {
+ $viewer = $this->getViewer();
+
+ $messages = id(new PhabricatorRepositoryStatusMessage())->loadAllWhere(
+ 'statusCode IN (%Ls)',
+ array(
+ PhabricatorRepositoryStatusMessage::CODE_ERROR,
+ ));
+
+ $repository_ids = mpull($messages, 'getRepositoryID');
+ if ($repository_ids) {
+ // NOTE: We're bypassing policies when loading repositories because we
+ // want to show errors exist even if the viewer can't see the repository.
+ // We use handles to describe the repository below, so the viewer won't
+ // actually be able to see any particulars if they can't see the
+ // repository.
+ $repositories = id(new PhabricatorRepositoryQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withIDs($repository_ids)
+ ->execute();
+ $repositories = mpull($repositories, null, 'getID');
+ }
+
+ $rows = array();
+ foreach ($messages as $message) {
+ $repository = idx($repositories, $message->getRepositoryID());
+ if (!$repository) {
+ continue;
+ }
+
+ if (!$repository->isTracked()) {
+ continue;
+ }
+
+ $icon = id(new PHUIIconView())
+ ->setIcon('fa-exclamation-triangle red');
+
+ $rows[] = array(
+ $icon,
+ $viewer->renderHandle($repository->getPHID()),
+ phutil_tag(
+ 'a',
+ array(
+ 'href' => $repository->getPathURI('manage/status/'),
+ ),
+ $message->getStatusTypeName()),
+ );
+ }
+
+ return id(new AphrontTableView($rows))
+ ->setNoDataString(
+ pht('No active repositories have outstanding errors.'))
+ ->setHeaders(
+ array(
+ null,
+ pht('Repository'),
+ pht('Error'),
+ ))
+ ->setColumnClasses(
+ array(
+ null,
+ 'pri',
+ 'wide',
+ ));
+ }
+
}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php b/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php
index eb5bb9688c..79d75cc50c 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryStatusMessage.php
@@ -1,43 +1,54 @@
<?php
final class PhabricatorRepositoryStatusMessage
extends PhabricatorRepositoryDAO {
const TYPE_INIT = 'init';
const TYPE_FETCH = 'fetch';
const TYPE_NEEDS_UPDATE = 'needs-update';
const CODE_ERROR = 'error';
const CODE_WORKING = 'working';
const CODE_OKAY = 'okay';
protected $repositoryID;
protected $statusType;
protected $statusCode;
protected $parameters = array();
protected $epoch;
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'parameters' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'statusType' => 'text32',
'statusCode' => 'text32',
),
self::CONFIG_KEY_SCHEMA => array(
'repositoryID' => array(
'columns' => array('repositoryID', 'statusType'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
public function getParameter($key, $default = null) {
return idx($this->parameters, $key, $default);
}
+ public function getStatusTypeName() {
+ $names = array(
+ self::TYPE_INIT => pht('Error While Initializing Repository'),
+ self::TYPE_FETCH => pht('Error While Fetching Changes'),
+ self::TYPE_NEEDS_UPDATE => pht('Repository Needs Update'),
+ );
+
+ $type = $this->getStatusType();
+ return idx($names, $type, $type);
+ }
+
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 11, 1:00 AM (1 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
141543
Default Alt Text
(13 KB)

Event Timeline