Get Geo secondaries nodes statuses over AJAX

parent a67245a8
...@@ -43,6 +43,8 @@ import GroupsList from './groups_list'; ...@@ -43,6 +43,8 @@ import GroupsList from './groups_list';
import ProjectsList from './projects_list'; import ProjectsList from './projects_list';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown'; import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import GeoNodes from './geo_nodes';
const ShortcutsBlob = require('./shortcuts_blob'); const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout'); const UserCallout = require('./user_callout');
...@@ -348,6 +350,9 @@ const UserCallout = require('./user_callout'); ...@@ -348,6 +350,9 @@ const UserCallout = require('./user_callout');
case 'abuse_reports': case 'abuse_reports':
new gl.AbuseReports(); new gl.AbuseReports();
break; break;
case 'geo_nodes':
new GeoNodes($('.geo-nodes'));
break;
} }
break; break;
case 'dashboard': case 'dashboard':
......
/* eslint-disable no-new*/
import './smart_interval';
const healthyClass = 'geo-node-icon-healthy';
const unhealthyClass = 'geo-node-icon-unhealthy';
class GeoNodeStatus {
constructor(el) {
this.$el = $(el);
this.$icon = $('.js-geo-node-icon', this.$el);
this.$status = $('.js-geo-node-status', this.$el);
this.$repositoriesSynced = $('.js-repositories-synced', this.$status);
this.$repositoriesFailed = $('.js-repositories-failed', this.$status);
this.$lfsObjectsSynced = $('.js-lfs-objects-synced', this.$status);
this.$health = $('.js-health', this.$status);
this.endpoint = this.$el.data('status-url');
this.statusInterval = new gl.SmartInterval({
callback: this.getStatus.bind(this),
startingInterval: 30000,
maxInterval: 120000,
hiddenInterval: 240000,
incrementByFactorOf: 15000,
immediateExecution: true,
});
}
getStatus() {
$.getJSON(this.endpoint, (status) => {
this.setStatusIcon(status.healthy);
this.$repositoriesSynced.html(`${status.repositories_synced_count}/${status.repositories_count} (${status.repositories_synced_in_percentage})`);
this.$repositoriesFailed.html(status.repositories_failed_count);
this.$lfsObjectsSynced.html(`${status.lfs_objects_synced_count}/${status.lfs_objects_count} (${status.lfs_objects_synced_in_percentage})`);
this.$health.html(status.health);
this.$status.show();
});
}
setStatusIcon(healthy) {
if (healthy) {
this.$icon.removeClass(unhealthyClass)
.addClass(healthyClass)
.attr('title', 'Healthy');
} else {
this.$icon.removeClass(healthyClass)
.addClass(unhealthyClass)
.attr('title', 'Unhealthy');
}
this.$icon.tooltip('fixTitle');
}
}
class GeoNodes {
constructor(container) {
this.$container = $(container);
this.pollForSecondaryNodeStatus();
}
pollForSecondaryNodeStatus() {
$('.js-geo-secondary-node', this.$container).each((i, el) => {
new GeoNodeStatus(el);
});
}
}
export default GeoNodes;
class Admin::GeoNodesController < Admin::ApplicationController class Admin::GeoNodesController < Admin::ApplicationController
before_action :check_license, except: [:index, :destroy] before_action :check_license, except: [:index, :destroy]
before_action :load_node, only: [:destroy, :repair, :toggle] before_action :load_node, only: [:destroy, :repair, :toggle, :status]
def index def index
# Ensure all nodes are using their Presenter # Ensure all nodes are using their Presenter
...@@ -57,6 +57,16 @@ class Admin::GeoNodesController < Admin::ApplicationController ...@@ -57,6 +57,16 @@ class Admin::GeoNodesController < Admin::ApplicationController
redirect_to admin_geo_nodes_path redirect_to admin_geo_nodes_path
end end
def status
status = Geo::NodeStatusService.new.call(@node)
respond_to do |format|
format.json do
render json: GeoNodeStatusSerializer.new.represent(status)
end
end
end
private private
def geo_node_params def geo_node_params
......
...@@ -4,15 +4,10 @@ module EE ...@@ -4,15 +4,10 @@ module EE
if node.primary? if node.primary?
icon 'star fw', class: 'has-tooltip', title: 'Primary node' icon 'star fw', class: 'has-tooltip', title: 'Primary node'
else else
status = status = node.enabled? ? 'healthy' : 'disabled'
if node.enabled?
node.healthy? ? 'healthy' : 'unhealthy'
else
'disabled'
end
icon 'globe fw', icon 'globe fw',
class: "geo-node-icon-#{status} has-tooltip", class: "js-geo-node-icon geo-node-icon-#{status} has-tooltip",
title: status.capitalize title: status.capitalize
end end
end end
......
class GeoNodeStatus class GeoNodeStatus
include ActiveModel::Model include ActiveModel::Model
attr_accessor :id
attr_writer :health attr_writer :health
def health def health
......
class GeoNodeStatusEntity < Grape::Entity
include ActionView::Helpers::NumberHelper
expose :id
expose :healthy?, as: :healthy
expose :health do |node|
node.healthy? ? 'No Health Problems Detected' : node.health
end
expose :lfs_objects_total, as: :lfs_objects_count
expose :lfs_objects_synced, as: :lfs_objects_synced_count
expose :lfs_objects_synced_in_percentage do |node|
number_to_percentage(node.lfs_objects_synced_in_percentage, precision: 2)
end
expose :repositories_count
expose :repositories_failed_count
expose :repositories_synced_count
expose :repositories_synced_in_percentage do |node|
number_to_percentage(node.repositories_synced_in_percentage, precision: 2)
end
end
class GeoNodeStatusSerializer < BaseSerializer
entity GeoNodeStatusEntity
end
...@@ -5,10 +5,10 @@ module Geo ...@@ -5,10 +5,10 @@ module Geo
KEYS = %w(health repositories_count repositories_synced_count repositories_failed_count lfs_objects_total lfs_objects_synced).freeze KEYS = %w(health repositories_count repositories_synced_count repositories_failed_count lfs_objects_total lfs_objects_synced).freeze
def call(status_url) def call(geo_node)
values = values =
begin begin
response = self.class.get(status_url, headers: headers, timeout: timeout) response = self.class.get(geo_node.status_url, headers: headers, timeout: timeout)
if response.success? if response.success?
response.parsed_response.values_at(*KEYS) response.parsed_response.values_at(*KEYS)
...@@ -19,7 +19,7 @@ module Geo ...@@ -19,7 +19,7 @@ module Geo
[e.message] [e.message]
end end
GeoNodeStatus.new(KEYS.zip(values).to_h) GeoNodeStatus.new(KEYS.zip(values).to_h.merge(id: geo_node.id))
end end
private private
......
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Geo nodes (#{@nodes.count}) Geo nodes (#{@nodes.count})
%ul.well-list %ul.well-list.geo-nodes
- @nodes.each do |node| - @nodes.each do |node|
%li %li{ id: dom_id(node), class: ('js-geo-secondary-node' if node.secondary?), data: { status_url: status_admin_geo_node_path(node) } }
.list-item-name .list-item-name
%span %span
= node_status_icon(node) = node_status_icon(node)
...@@ -24,17 +24,21 @@ ...@@ -24,17 +24,21 @@
- if node.primary? - if node.primary?
%span.help-block Primary node %span.help-block Primary node
- else - else
%p .js-geo-node-status{ style: 'display: none' }
%span.help-block %p
Repositories synced: #{node.repositories_synced_count}/#{node.repositories_count} (#{number_to_percentage(node.repositories_synced_in_percentage, precision: 2)}) %span.help-block
%p Repositories synced:
%span.help-block %span.js-repositories-synced
Repositories failed: #{node.repositories_failed_count} %p
%p %span.help-block
%span.help-block Repositories failed:
LFS objects synced: #{node.lfs_objects_synced}/#{node.lfs_objects_total} (#{number_to_percentage(node.lfs_objects_synced_in_percentage, precision: 2)}) %span.js-repositories-failed
%p %p
%span.help-block= node.healthy? ? 'No Health Problems Detected' : node.health %span.help-block
LFS objects synced:
%span.js-lfs-objects-synced
%p
%span.help-block.js-health
.pull-right .pull-right
- if Gitlab::Geo.license_allows? - if Gitlab::Geo.license_allows?
......
...@@ -119,6 +119,7 @@ namespace :admin do ...@@ -119,6 +119,7 @@ namespace :admin do
member do member do
post :repair post :repair
post :toggle post :toggle
get :status
end end
end end
## EE-specific ## EE-specific
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment