Commit fdb7fab8 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre Committed by Nick Thomas

Geo - Selective replication allows admins to select any groups

parent 220f1ed5
...@@ -55,7 +55,6 @@ import UserCallout from './user_callout'; ...@@ -55,7 +55,6 @@ import UserCallout from './user_callout';
import ShortcutsWiki from './shortcuts_wiki'; import ShortcutsWiki from './shortcuts_wiki';
import Pipelines from './pipelines'; import Pipelines from './pipelines';
import BlobViewer from './blob/viewer/index'; import BlobViewer from './blob/viewer/index';
import GeoNodeForm from './geo/geo_node_form';
import GeoNodes from './geo_nodes'; import GeoNodes from './geo_nodes';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select'; import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import UsersSelect from './users_select'; import UsersSelect from './users_select';
...@@ -640,7 +639,9 @@ import initGroupAnalytics from './init_group_analytics'; ...@@ -640,7 +639,9 @@ import initGroupAnalytics from './init_group_analytics';
break; break;
case 'geo_nodes': case 'geo_nodes':
new GeoNodes($('.geo-nodes')); new GeoNodes($('.geo-nodes'));
new GeoNodeForm($('.js-geo-node-form')); import(/* webpackChunkName: 'geo_node_form' */ './geo/geo_node_form')
.then(geoNodeForm => geoNodeForm.default($('.js-geo-node-form')))
.catch(() => {});
break; break;
} }
break; break;
......
export default class GeoNodeForm { /* global Flash */
constructor(container) { import {
this.$container = container; s__,
this.$namespaces = this.$container.find('.js-hide-if-geo-primary'); } from '../locale';
this.$namespacesSelect = this.$namespaces.find('.select2'); import '../flash';
this.$primaryCheckbox = this.$container.find("input[type='checkbox']"); import Api from '../api';
this.$primaryCheckbox.on('change', () => this.onPrimaryCheckboxChange());
} const onPrimaryCheckboxChange = function onPrimaryCheckboxChange(e, $namespaces) {
const $namespacesSelect = $('.select2', $namespaces);
onPrimaryCheckboxChange() {
this.$namespacesSelect.select2('data', null); $namespacesSelect.select2('data', null);
this.$namespaces.toggleClass('hidden', this.$primaryCheckbox.is(':checked')); $namespaces.toggleClass('hidden', e.currentTarget.checked);
} };
export default function geoNodeForm($container) {
const $namespaces = $('.js-hide-if-geo-primary', $container);
const $primaryCheckbox = $('input[type="checkbox"]', $container);
const $select2Dropdown = $('.js-geo-node-namespaces', $container);
$primaryCheckbox.on('change', e => onPrimaryCheckboxChange(e, $namespaces));
$select2Dropdown.select2({
placeholder: s__('Geo|Select groups to replicate.'),
multiple: true,
initSelection($el, callback) {
callback($el.data('selected'));
},
ajax: {
url: Api.buildUrl(Api.groupsPath),
dataType: 'JSON',
quietMillis: 250,
data(search) {
return {
search,
};
},
results(data) {
return {
results: data.map(group => ({
id: group.id,
text: group.full_name,
})),
};
},
},
});
} }
module Geo
class NodeCreateService
attr_reader :params
def initialize(params)
@params = params.dup
@params[:namespace_ids] = @params[:namespace_ids].to_s.split(',')
end
def execute
GeoNode.create(params).persisted?
end
end
end
...@@ -6,6 +6,7 @@ module Geo ...@@ -6,6 +6,7 @@ module Geo
@geo_node = geo_node @geo_node = geo_node
@old_namespace_ids = geo_node.namespace_ids @old_namespace_ids = geo_node.namespace_ids
@params = params.slice(:url, :primary, :namespace_ids) @params = params.slice(:url, :primary, :namespace_ids)
@params[:namespace_ids] = @params[:namespace_ids].to_s.split(',')
end end
def execute def execute
...@@ -15,7 +16,7 @@ module Geo ...@@ -15,7 +16,7 @@ module Geo
Geo::RepositoriesChangedEventStore.new(geo_node).create Geo::RepositoriesChangedEventStore.new(geo_node).create
end end
geo_node true
end end
private private
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
= link_to 'here', help_page_path('gitlab-geo/configuration.html', anchor: 'step-5-enabling-the-secondary-gitlab-node') = link_to 'here', help_page_path('gitlab-geo/configuration.html', anchor: 'step-5-enabling-the-secondary-gitlab-node')
.form-group.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) } .form-group.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) }
= form.label :namespace_ids, 'Namespaces to replicate', class: 'control-label' = form.label :namespace_ids, s_('Geo|Groups to replicate'), class: 'control-label'
.col-sm-10 .col-sm-10
= form.select :namespace_ids, namespaces_options(geo_node.namespace_ids), { include_hidden: true }, multiple: true, class: 'select2 select-wide', data: { field: 'namespace_ids' } = hidden_field_tag "#{form.object_name}[namespace_ids]", geo_node.namespace_ids.join(","), class: 'js-geo-node-namespaces', data: { selected: node_namespaces_options(geo_node.namespaces).to_json }
.help-block .help-block
Choose which namespaces you wish to replicate to this secondary node. Leave blank to replicate all. #{ s_("Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all.") }
---
title: Geo - Selective replication allows admins to select any groups
merge_request: 2779
author:
type: fixed
...@@ -281,7 +281,7 @@ Point your users to the [after setup steps](after_setup.md). ...@@ -281,7 +281,7 @@ Point your users to the [after setup steps](after_setup.md).
## Selective replication ## Selective replication
With GitLab 9.5, GitLab Geo now supports the first iteration of selective With GitLab 9.5, GitLab Geo now supports the first iteration of selective
replication, which allows admins to choose which namespaces should be replication, which allows admins to choose which groups should be
replicated by secondary nodes. replicated by secondary nodes.
It is important to notice that selective replication: It is important to notice that selective replication:
...@@ -291,7 +291,7 @@ It is important to notice that selective replication: ...@@ -291,7 +291,7 @@ It is important to notice that selective replication:
relies on PostgreSQL replication, all project metadata gets replicated to relies on PostgreSQL replication, all project metadata gets replicated to
secondary nodes, but repositories that have not been selected will be empty. secondary nodes, but repositories that have not been selected will be empty.
1. Secondary nodes won't pull repositories that do not belong to the selected 1. Secondary nodes won't pull repositories that do not belong to the selected
namespaces to be replicated. groups to be replicated.
## Adding another secondary Geo node ## Adding another secondary Geo node
......
...@@ -14,9 +14,7 @@ class Admin::GeoNodesController < Admin::ApplicationController ...@@ -14,9 +14,7 @@ class Admin::GeoNodesController < Admin::ApplicationController
end end
def create def create
@node = GeoNode.new(geo_node_params) if Geo::NodeCreateService.new(geo_node_params).execute
if @node.save
redirect_to admin_geo_nodes_path, notice: 'Node was successfully created.' redirect_to admin_geo_nodes_path, notice: 'Node was successfully created.'
else else
@nodes = GeoNode.all @nodes = GeoNode.all
...@@ -26,9 +24,9 @@ class Admin::GeoNodesController < Admin::ApplicationController ...@@ -26,9 +24,9 @@ class Admin::GeoNodesController < Admin::ApplicationController
def update def update
if Geo::NodeUpdateService.new(@node, geo_node_params).execute if Geo::NodeUpdateService.new(@node, geo_node_params).execute
redirect_to admin_geo_nodes_path, notice: 'Geo Node was successfully updated.' redirect_to admin_geo_nodes_path, notice: 'Node was successfully updated.'
else else
render 'edit' render :edit
end end
end end
...@@ -79,7 +77,7 @@ class Admin::GeoNodesController < Admin::ApplicationController ...@@ -79,7 +77,7 @@ class Admin::GeoNodesController < Admin::ApplicationController
private private
def geo_node_params def geo_node_params
params.require(:geo_node).permit(:url, :primary, namespace_ids: [], geo_node_key_attributes: [:key]) params.require(:geo_node).permit(:url, :primary, :namespace_ids, geo_node_key_attributes: [:key])
end end
def check_license def check_license
......
module EE module EE
module GeoHelper module GeoHelper
def node_namespaces_options(namespaces)
namespaces.map { |g| { id: g.id, text: g.full_name } }
end
def node_selected_namespaces_to_replicate(node) def node_selected_namespaces_to_replicate(node)
node.namespaces.map(&:human_name).sort.join(', ') node.namespaces.map(&:human_name).sort.join(', ')
end end
......
...@@ -8,8 +8,8 @@ msgid "" ...@@ -8,8 +8,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: gitlab 1.0.0\n" "Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-10-02 15:48-0400\n" "POT-Creation-Date: 2017-10-04 17:48+0100\n"
"PO-Revision-Date: 2017-10-02 15:48-0400\n" "PO-Revision-Date: 2017-10-04 17:48+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n" "Language: \n"
...@@ -382,6 +382,9 @@ msgstr "" ...@@ -382,6 +382,9 @@ msgstr ""
msgid "Cherry-pick this merge request" msgid "Cherry-pick this merge request"
msgstr "" msgstr ""
msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
msgstr ""
msgid "CiStatusLabel|canceled" msgid "CiStatusLabel|canceled"
msgstr "" msgstr ""
...@@ -712,6 +715,12 @@ msgstr "" ...@@ -712,6 +715,12 @@ msgstr ""
msgid "Geo Nodes" msgid "Geo Nodes"
msgstr "" msgstr ""
msgid "Geo|Groups to replicate"
msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Git storage health information has been reset" msgid "Git storage health information has been reset"
msgstr "" msgstr ""
...@@ -1195,6 +1204,21 @@ msgstr "" ...@@ -1195,6 +1204,21 @@ msgstr ""
msgid "ProjectNetworkGraph|Graph" msgid "ProjectNetworkGraph|Graph"
msgstr "" msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
msgstr ""
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
msgstr ""
msgid "ProjectsDropdown|Frequently visited" msgid "ProjectsDropdown|Frequently visited"
msgstr "" msgstr ""
......
...@@ -105,8 +105,10 @@ describe Admin::GeoNodesController, :postgresql do ...@@ -105,8 +105,10 @@ describe Admin::GeoNodesController, :postgresql do
allow(Gitlab::Geo).to receive(:license_allows?).and_return(true) allow(Gitlab::Geo).to receive(:license_allows?).and_return(true)
end end
it 'creates the node' do it 'delegates the create of the Geo node to Geo::NodeCreateService' do
expect { go }.to change { GeoNode.count }.by(1) expect_any_instance_of(Geo::NodeCreateService).to receive(:execute).once.and_call_original
go
end end
end end
end end
......
require 'spec_helper'
describe Geo::NodeCreateService do
describe '#execute' do
it 'creates a new node with valid params' do
params = { url: 'http://example.com', geo_node_key_attributes: attributes_for(:key) }
service = described_class.new(params)
expect { service.execute }.to change(GeoNode, :count).by(1)
end
it 'does not create a node with invalid params' do
service = described_class.new({ url: 'http://example.com' })
expect { service.execute }.not_to change(GeoNode, :count)
end
it 'returns true when creation succeeds' do
params = { url: 'http://example.com', geo_node_key_attributes: attributes_for(:key) }
service = described_class.new(params)
expect(service.execute).to eq true
end
it 'returns false when creation fails' do
params = { url: 'http://example.com' }
service = described_class.new(params)
expect(service.execute).to eq false
end
it 'parses the namespace_ids when node have namespace restrictions' do
groups = create_list(:group, 2)
params = { url: 'http://example.com', geo_node_key_attributes: attributes_for(:key), namespace_ids: groups.map(&:id).join(',') }
service = described_class.new(params)
service.execute
expect(GeoNode.last.namespace_ids).to match_array(groups.map(&:id))
end
end
end
require 'spec_helper' require 'spec_helper'
describe Geo::NodeUpdateService do describe Geo::NodeUpdateService do
let(:group) { create(:group) } let(:groups) { create_list(:group, 2) }
let!(:primary) { create(:geo_node, :primary) } let!(:primary) { create(:geo_node, :primary) }
let(:geo_node) { create(:geo_node) } let(:geo_node) { create(:geo_node) }
let(:geo_node_with_restrictions) { create(:geo_node, namespace_ids: [group.id]) } let(:geo_node_with_restrictions) { create(:geo_node, namespace_ids: [groups.first.id]) }
describe '#execute' do describe '#execute' do
it 'updates the node without changing the key' do it 'updates the node without changing the key' do
...@@ -19,6 +19,12 @@ describe Geo::NodeUpdateService do ...@@ -19,6 +19,12 @@ describe Geo::NodeUpdateService do
expect(geo_node.geo_node_key.fingerprint).to eq(original_fingerprint) expect(geo_node.geo_node_key.fingerprint).to eq(original_fingerprint)
end end
it 'returns true when update succeeds' do
service = described_class.new(geo_node, { url: 'http://example.com' })
expect(service.execute).to eq true
end
it 'returns false when update fails' do it 'returns false when update fails' do
allow(geo_node).to receive(:update).and_return(false) allow(geo_node).to receive(:update).and_return(false)
...@@ -28,19 +34,19 @@ describe Geo::NodeUpdateService do ...@@ -28,19 +34,19 @@ describe Geo::NodeUpdateService do
end end
it 'logs an event to the Geo event log when namespaces change' do it 'logs an event to the Geo event log when namespaces change' do
service = described_class.new(geo_node, namespace_ids: [group.id]) service = described_class.new(geo_node, namespace_ids: groups.map(&:id).join(','))
expect { service.execute }.to change(Geo::RepositoriesChangedEvent, :count).by(1) expect { service.execute }.to change(Geo::RepositoriesChangedEvent, :count).by(1)
end end
it 'does not log an event to the Geo event log when removing namespace restrictions' do it 'does not log an event to the Geo event log when removing namespace restrictions' do
service = described_class.new(geo_node_with_restrictions, namespace_ids: []) service = described_class.new(geo_node_with_restrictions, namespace_ids: '')
expect { service.execute }.not_to change(Geo::RepositoriesChangedEvent, :count) expect { service.execute }.not_to change(Geo::RepositoriesChangedEvent, :count)
end end
it 'does not log an event to the Geo event log when node is a primary node' do it 'does not log an event to the Geo event log when node is a primary node' do
service = described_class.new(primary, namespace_ids: [group.id]) service = described_class.new(primary, namespace_ids: groups.map(&:id).join(','))
expect { service.execute }.not_to change(Geo::RepositoriesChangedEvent, :count) expect { service.execute }.not_to change(Geo::RepositoriesChangedEvent, :count)
end end
......
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