Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F93076079
PhabricatorConfigClusterRepositoriesController.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Nov 26, 00:51
Size
11 KB
Mime Type
text/x-php
Expires
Thu, Nov 28, 00:51 (1 d, 23 h)
Engine
blob
Format
Raw Data
Handle
22568413
Attached To
rPH Phabricator
PhabricatorConfigClusterRepositoriesController.php
View Options
<?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
(
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
());
// Filter out any versions for devices which are no longer active.
foreach
(
$versions
as
$key
=>
$version
)
{
$version_device_phid
=
$version
->
getDevicePHID
();
if
(
empty
(
$active_devices
[
$version_device_phid
]))
{
unset
(
$versions
[
$key
]);
}
}
$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'
,
));
}
}
Event Timeline
Log In to Comment