Commit 2903b81a authored by Zack Cuddy's avatar Zack Cuddy

Remove enable_geo_node_form_js feature flag

The Geo Node Form has been re-written
in Vue.

This removes the need for the Geo
Node Form Feature Flag.

The MR also deleted the legacy
code for the old Geo Node Form.
parent 9de8ad7f
......@@ -111,14 +111,6 @@ module ApplicationSettingsHelper
]
end
def repository_storages_options_for_select(selected)
options = Gitlab.config.repositories.storages.map do |name, storage|
["#{name} - #{storage['gitaly_address']}", name]
end
options_for_select(options, selected)
end
def repository_storages_options_json
options = Gitlab.config.repositories.storages.map do |name, storage|
{
......
......@@ -125,6 +125,7 @@ export default {
<section class="d-flex align-items-center mt-4">
<gl-deprecated-button
id="node-save-button"
data-qa-selector="add_node_button"
variant="success"
@click="saveGeoNode(nodeData)"
>{{ saveButtonTitle }}</gl-deprecated-button
......
......@@ -72,6 +72,7 @@ export default {
<gl-form-input
id="node-name-field"
v-model="nodeData.name"
data-qa-selector="node_name_field"
type="text"
@blur="blur('name')"
/>
......@@ -84,7 +85,13 @@ export default {
:state="validUrl"
:invalid-feedback="errors.url"
>
<gl-form-input id="node-url-field" v-model="nodeData.url" type="text" @blur="blur('url')" />
<gl-form-input
id="node-url-field"
v-model="nodeData.url"
data-qa-selector="node_url_field"
type="text"
@blur="blur('url')"
/>
</gl-form-group>
</section>
</template>
import initForm from '../shared/init_form';
document.addEventListener('DOMContentLoaded', initForm);
import initForm from '../shared/init_form';
import initGeoNodeForm from 'ee/geo_node_form';
if (gon.features?.enableGeoNodeFormJs) {
document.addEventListener('DOMContentLoaded', initGeoNodeForm);
} else {
document.addEventListener('DOMContentLoaded', initForm);
}
document.addEventListener('DOMContentLoaded', initGeoNodeForm);
import initForm from '../shared/init_form';
import initGeoNodeForm from 'ee/geo_node_form';
if (gon.features?.enableGeoNodeFormJs) {
document.addEventListener('DOMContentLoaded', initGeoNodeForm);
} else {
document.addEventListener('DOMContentLoaded', initForm);
}
document.addEventListener('DOMContentLoaded', initGeoNodeForm);
import $ from 'jquery';
import { s__ } from '~/locale';
import '~/flash';
import Api from '~/api';
const onPrimaryCheckboxChange = function onPrimaryCheckboxChange(e, $namespaces, $reverification) {
const $namespacesSelect = $('.select2', $namespaces);
const $internalUrl = $('.js-internal-url');
$namespacesSelect.select2('data', null);
$internalUrl.toggleClass('hidden', !e.currentTarget.checked);
$namespaces.toggleClass('hidden', e.currentTarget.checked);
$reverification.toggleClass('hidden', !e.currentTarget.checked);
};
const onSelectiveSyncTypeChange = function onSelectiveSyncTypeChange(e, $byNamespaces, $byShards) {
$byNamespaces.toggleClass('hidden', e.target.value !== 'namespaces');
$byShards.toggleClass('hidden', e.target.value !== 'shards');
};
export default function geoNodeForm() {
const $container = $('.js-geo-node-form');
const $namespaces = $('.js-hide-if-geo-primary', $container);
const $reverification = $('.js-hide-if-geo-secondary', $container);
const $primaryCheckbox = $('input#geo_node_primary', $container);
const $selectiveSyncTypeSelect = $('.js-geo-node-selective-sync-type', $container);
const $select2Dropdown = $('.js-geo-node-namespaces', $container);
const $syncByNamespaces = $('.js-sync-by-namespace', $container);
const $syncByShards = $('.js-sync-by-shard', $container);
$primaryCheckbox.on('change', e => onPrimaryCheckboxChange(e, $namespaces, $reverification));
$selectiveSyncTypeSelect.on('change', e =>
onSelectiveSyncTypeChange(e, $syncByNamespaces, $syncByShards),
);
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
$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,
})),
};
},
},
});
})
.catch(() => {});
}
import initForm from '../shared/init_form';
document.addEventListener('DOMContentLoaded', initForm);
......@@ -3,7 +3,6 @@
class Admin::Geo::NodesController < Admin::Geo::ApplicationController
before_action :check_license!, except: :index
before_action :load_node, only: [:edit, :update]
before_action :push_feature_flag, except: :index
# rubocop: disable CodeReuse/ActiveRecord
def index
......@@ -25,12 +24,18 @@ class Admin::Geo::NodesController < Admin::Geo::ApplicationController
else
@nodes = GeoNode.all
render :new
render :form
end
end
def new
@node = GeoNode.new
@form_title = _('New Geo Node')
render :form
end
def edit
@form_title = _('Edit Geo Node')
render :form
end
def update
......@@ -38,7 +43,7 @@ class Admin::Geo::NodesController < Admin::Geo::ApplicationController
flash[:toast] = _('Node was successfully updated.')
redirect_to admin_geo_nodes_path
else
render :edit
render :form
end
end
......@@ -66,8 +71,4 @@ class Admin::Geo::NodesController < Admin::Geo::ApplicationController
@node = GeoNode.find(params[:id])
@serialized_node = GeoNodeSerializer.new.represent(@node).to_json
end
def push_feature_flag
push_frontend_feature_flag(:enable_geo_node_form_js)
end
end
......@@ -54,17 +54,6 @@ module EE
end
end
def selective_sync_type_options_for_select(geo_node)
options_for_select(
[
[s_('Geo|All projects'), ''],
[s_('Geo|Projects in certain groups'), 'namespaces'],
[s_('Geo|Projects in certain storage shards'), 'shards']
],
geo_node.selective_sync_type
)
end
def selective_sync_types_json
options = {
ALL: {
......
= form_errors(geo_node)
.form-row.form-group
.form-group.col-sm-6
= form.label :name, _('Name'), class: 'font-weight-bold'
= form.text_field :name, class: 'form-control qa-node-name-field'
.form-text.text-muted= _('The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url` with a trailing slash')
.form-group.col-sm-6
= form.label :url, s_('Geo|URL'), class: 'font-weight-bold'
= form.text_field :url, class: 'form-control qa-node-url-field'
.form-text.text-muted= _('The user-facing URL of the Geo node')
.form-group.row
.col-sm-10
.form-check
= form.check_box :primary, class: 'form-check-input'
= form.label :primary, class: 'form-check-label' do
%span= s_('Geo|This is a primary node')
.form-row.form-group.js-internal-url{ class: ('hidden' unless geo_node.primary?) }
.col-sm-6
= form.label :internal_url, s_('Geo|Internal URL (optional)'), class: 'font-weight-bold'
= form.text_field :internal_url, class: 'form-control'
.form-text.text-muted= s_('Geo|The URL defined on the primary node that secondary nodes should use to contact it. Defaults to URL')
.form-row.form-group.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) }
.col-sm-4
= form.label :selective_sync_type, s_('Geo|Selective synchronization'), class: 'font-weight-bold'
= form.select :selective_sync_type, selective_sync_type_options_for_select(geo_node), {}, { class: "form-control js-geo-node-selective-sync-type" }
.form-row.form-group.js-sync-by-namespace{ class: ('hidden' unless geo_node.selective_sync_by_namespaces?) }
.col-sm-4
= form.label :namespace_ids, s_('Geo|Groups to synchronize'), class: 'font-weight-bold'
= 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 }
.form-text.text-muted= s_('Geo|Choose which groups you wish to synchronize to this secondary node.')
.form-row.form-group.js-sync-by-shard{ class: ('hidden' unless geo_node.selective_sync_by_shards?) }
.col-sm-4
= form.label :selective_sync_shards, s_('Geo|Shards to synchronize'), class: 'font-weight-bold'
= form.select :selective_sync_shards, repository_storages_options_for_select(geo_node.selective_sync_shards), { include_hidden: false }, multiple: true, class: 'form-control'
.form-text.text-muted= s_('Choose which shards you wish to synchronize to this secondary node.')
.form-row.form-group.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) }
.col-sm-8
= form.label :repos_max_capacity, s_('Geo|Repository sync capacity'), class: 'font-weight-bold'
= form.number_field :repos_max_capacity, class: 'form-control col-sm-2', min: 0
.form-text.text-muted= s_('Control the maximum concurrency of repository backfill for this secondary node')
.form-row.form-group.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) }
.col-sm-8
= form.label :files_max_capacity, s_('Geo|File sync capacity'), class: 'font-weight-bold'
= form.number_field :files_max_capacity, class: 'form-control col-sm-2', min: 0
.form-text.text-muted= s_('Geo|Control the maximum concurrency of LFS/attachment backfill for this secondary node')
.form-row.form-group.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) }
.col-sm-8
= form.label :container_repositories_max_capacity, s_('Geo|Container repositories sync capacity'), class: 'font-weight-bold'
= form.number_field :container_repositories_max_capacity, class: 'form-control col-sm-2', min: 0
.form-text.text-muted= s_('Geo|Control the maximum concurrency of container repository operations for this Geo node')
.form-row.form-group
.col-sm-8
= form.label :verification_max_capacity, s_('Geo|Verification capacity'), class: 'font-weight-bold'
= form.number_field :verification_max_capacity, class: 'form-control col-sm-2', min: 0
.form-text.text-muted= s_('Geo|Control the maximum concurrency of verification operations for this Geo node')
.form-row.form-group.js-hide-if-geo-secondary{ class: ('hidden' unless geo_node.primary?) }
.col-sm-8
= form.label :minimum_reverification_interval, s_('Geo|Re-verification interval'), class: 'font-weight-bold'
= form.number_field :minimum_reverification_interval, class: 'form-control col-sm-2', min: 1
.form-text.text-muted= s_('Geo|Control the minimum interval in days that a repository should be reverified for this primary node')
.form-group.row.js-hide-if-geo-primary{ class: ('hidden' unless geo_node.secondary?) }
.col-sm-10
= form.label :sync_object_storage, _('Object Storage replication'), class: 'label-bold'
.form-check
= form.check_box :sync_object_storage, class: 'form-check-input'
= form.label :sync_object_storage, class: 'form-check-label' do
%span= s_('Geo|Allow this secondary node to replicate content on Object Storage')
.form-text.text-muted= s_('Geo|If enabled, and if object storage is enabled, GitLab will handle Object Storage replication using Geo')
- page_title _('Edit Geo Node')
- if Feature.enabled?(:enable_geo_node_form_js)
#js-geo-node-form{ data: { "selective-sync-types" => selective_sync_types_json,
"sync-shards-options" => repository_storages_options_json,
"node-data" => @serialized_node } }
- else
%h3.page-title
Edit Geo Node
= form_for [:admin, @node], html: { class: 'js-geo-node-form' } do |f|
= render partial: 'form', locals: { form: f, geo_node: @node }
.form-actions
= f.submit 'Save changes', class: 'btn btn-success'
= link_to 'Cancel', admin_geo_nodes_path, class: 'btn btn-cancel'
%hr
- page_title @form_title
#js-geo-node-form{ data: { "selective-sync-types" => selective_sync_types_json,
"sync-shards-options" => repository_storages_options_json,
"node-data" => @serialized_node } }
- page_title _('New Geo Node')
- if Feature.enabled?(:enable_geo_node_form_js)
#js-geo-node-form{ data: { "selective-sync-types" => selective_sync_types_json,
"sync-shards-options" => repository_storages_options_json } }
- else
%h2.page-title
%span.title-text
New node
%hr.page-title-separator
- if Gitlab::Geo.license_allows?
= form_for [:admin, @node], as: :geo_node, url: admin_geo_nodes_path, html: { class: 'js-geo-node-form' } do |f|
= render partial: 'form', locals: { form: f, geo_node: @node }
.form-actions
= f.submit 'Add Node', class: 'btn btn-success qa-add-node-button'
= link_to 'Cancel', admin_geo_nodes_path, class: 'btn btn-cancel'
......@@ -5,9 +5,20 @@ require 'spec_helper'
describe 'admin Geo Nodes', :js, :geo do
let!(:geo_node) { create(:geo_node) }
def expect_fields(node_fields)
node_fields.each do |field|
expect(page).to have_field(field)
end
end
def expect_no_fields(node_fields)
node_fields.each do |field|
expect(page).not_to have_field(field)
end
end
before do
allow(Gitlab::Geo).to receive(:license_allows?).and_return(true)
stub_feature_flags(enable_geo_node_form_js: false)
sign_in(create(:admin))
end
......@@ -79,73 +90,81 @@ describe 'admin Geo Nodes', :js, :geo do
end
it 'creates a new Geo Node' do
check 'This is a primary node'
fill_in 'geo_node_name', with: 'a node name'
fill_in 'geo_node_url', with: 'https://test.gitlab.com'
click_button 'Add Node'
check 'node-primary-field'
fill_in 'node-name-field', with: 'a node name'
fill_in 'node-url-field', with: 'https://test.gitlab.com'
click_button 'Save'
expect(current_path).to eq admin_geo_nodes_path
wait_for_requests
expect(current_path).to eq admin_geo_nodes_path
page.within(find('.card', match: :first)) do
expect(page).to have_content(geo_node.url)
end
end
it 'toggles the visibility of secondary only params based on primary node checkbox' do
primary_only_fields = [
'Internal URL (optional)',
'Re-verification interval'
]
secondary_only_fields = [
'Selective synchronization',
'Repository sync capacity',
'File sync capacity',
'Object Storage replication'
]
context 'toggles the visibility of secondary only params based on primary node checkbox' do
primary_only_fields = %w(node-internal-url-field node-reverification-interval-field)
secondary_only_fields = %w(node-selective-synchronization-field node-repository-capacity-field node-file-capacity-field node-object-storage-field)
expect(page).to have_unchecked_field('This is a primary node')
context 'by default' do
it 'node primary field is unchecked' do
expect(page).to have_unchecked_field('node-primary-field')
end
primary_only_fields.each do |field|
expect(page).to have_field(field, visible: false)
end
it 'renders no primary fields' do
expect_no_fields(primary_only_fields)
end
secondary_only_fields.each do |field|
expect(page).to have_field(field)
it 'renders all secondary fields' do
expect_fields(secondary_only_fields)
end
end
check 'This is a primary node'
context 'when node primary field gets checked' do
before do
check 'node-primary-field'
end
primary_only_fields.each do |field|
expect(page).to have_field(field)
end
it 'renders all primary fields' do
expect_fields(primary_only_fields)
end
secondary_only_fields.each do |field|
expect(page).to have_field(field, visible: false)
it 'renders no secondary fields' do
expect_no_fields(secondary_only_fields)
end
end
uncheck 'This is a primary node'
context 'when node primary field gets unchecked' do
before do
uncheck 'node-primary-field'
end
primary_only_fields.each do |field|
expect(page).to have_field(field, visible: false)
end
it 'renders no primary fields' do
expect_no_fields(primary_only_fields)
end
secondary_only_fields.each do |field|
expect(page).to have_field(field)
it 'renders all secondary fields' do
expect_fields(secondary_only_fields)
end
end
end
it 'returns an error message when a duplicate primary is added' do
create(:geo_node, :primary)
context 'with an existing primary node' do
before do
create(:geo_node, :primary)
end
check 'This is a primary node'
fill_in 'geo_node_url', with: 'https://another-primary.example.com'
click_button 'Add Node'
it 'returns an error message when a another primary is attempted to be added' do
check 'node-primary-field'
fill_in 'node-url-field', with: 'https://another-primary.example.com'
click_button 'Save'
expect(current_path).to eq admin_geo_nodes_path
wait_for_requests
expect(current_path).to eq new_admin_geo_node_path
expect(page).to have_content('Primary node already exists')
expect(page).to have_content('There was an error saving this Geo Node')
end
end
end
......@@ -155,13 +174,13 @@ describe 'admin Geo Nodes', :js, :geo do
visit edit_admin_geo_node_path(geo_node)
fill_in 'URL', with: 'http://newsite.com'
fill_in 'Internal URL', with: 'http://internal-url.com'
check 'This is a primary node'
click_button 'Save changes'
fill_in 'node-url-field', with: 'http://newsite.com'
fill_in 'node-internal-url-field', with: 'http://internal-url.com'
check 'node-primary-field'
click_button 'Update'
expect(current_path).to eq admin_geo_nodes_path
wait_for_requests
expect(current_path).to eq admin_geo_nodes_path
page.within(find('.card', match: :first)) do
expect(page).to have_content('http://newsite.com')
......@@ -214,45 +233,20 @@ describe 'admin Geo Nodes', :js, :geo do
end
end
describe 'Feature(:enable_geo_node_form_js)' do
describe 'when true' do
before do
stub_feature_flags(enable_geo_node_form_js: true)
end
it '`/new` uses the Vue form instead of the HAML partial' do
visit new_admin_geo_node_path
describe 'Geo node form routes' do
routes = []
expect(page).to have_css(".geo-node-form-container")
expect(page).not_to have_css(".js-geo-node-form")
end
before do
routes = [{ path: new_admin_geo_node_path, slug: '/new' }, { path: edit_admin_geo_node_path(geo_node), slug: '/edit' }]
end
it '`/edit` uses the Vue form instead of the HAML partial' do
visit edit_admin_geo_node_path(geo_node)
routes.each do |route|
it "#{route.slug} renders the geo form" do
visit route.path
expect(page).to have_css(".geo-node-form-container")
expect(page).not_to have_css(".js-geo-node-form")
end
end
describe 'when false' do
before do
stub_feature_flags(enable_geo_node_form_js: false)
end
it '`/new` uses the HAML partial instead of the Vue form' do
visit new_admin_geo_node_path
expect(page).not_to have_css(".geo-node-form-container")
expect(page).to have_css(".js-geo-node-form")
end
it '`/edit` uses the HAML partial instead of the Vue form' do
visit edit_admin_geo_node_path(geo_node)
expect(page).not_to have_css(".geo-node-form-container")
expect(page).to have_css(".js-geo-node-form")
end
end
end
end
......@@ -3791,9 +3791,6 @@ msgstr ""
msgid "Choose which shards you wish to synchronize to this secondary node"
msgstr ""
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "Choose which status most accurately reflects the current state of this issue:"
msgstr ""
......@@ -9407,30 +9404,9 @@ msgstr ""
msgid "Geo|All projects are being scheduled for re-verify"
msgstr ""
msgid "Geo|Allow this secondary node to replicate content on Object Storage"
msgstr ""
msgid "Geo|Batch operations"
msgstr ""
msgid "Geo|Choose which groups you wish to synchronize to this secondary node."
msgstr ""
msgid "Geo|Container repositories sync capacity"
msgstr ""
msgid "Geo|Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr ""
msgid "Geo|Control the maximum concurrency of container repository operations for this Geo node"
msgstr ""
msgid "Geo|Control the maximum concurrency of verification operations for this Geo node"
msgstr ""
msgid "Geo|Control the minimum interval in days that a repository should be reverified for this primary node"
msgstr ""
msgid "Geo|Could not remove tracking entry for an existing project."
msgstr ""
......@@ -9440,24 +9416,12 @@ msgstr ""
msgid "Geo|Failed"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
msgid "Geo|Geo Status"
msgstr ""
msgid "Geo|Groups to synchronize"
msgstr ""
msgid "Geo|If enabled, and if object storage is enabled, GitLab will handle Object Storage replication using Geo"
msgstr ""
msgid "Geo|In sync"
msgstr ""
msgid "Geo|Internal URL (optional)"
msgstr ""
msgid "Geo|Last repository check run"
msgstr ""
......@@ -9503,9 +9467,6 @@ msgstr ""
msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Re-verification interval"
msgstr ""
msgid "Geo|Redownload"
msgstr ""
......@@ -9518,9 +9479,6 @@ msgstr ""
msgid "Geo|Remove tracking database entry"
msgstr ""
msgid "Geo|Repository sync capacity"
msgstr ""
msgid "Geo|Resync"
msgstr ""
......@@ -9536,15 +9494,6 @@ msgstr ""
msgid "Geo|Reverify all projects"
msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
msgid "Geo|Selective synchronization"
msgstr ""
msgid "Geo|Shards to synchronize"
msgstr ""
msgid "Geo|Status"
msgstr ""
......@@ -9557,18 +9506,12 @@ msgstr ""
msgid "Geo|Synchronization failed - %{error}"
msgstr ""
msgid "Geo|The URL defined on the primary node that secondary nodes should use to contact it. Defaults to URL"
msgstr ""
msgid "Geo|The database is currently %{db_lag} behind the primary node."
msgstr ""
msgid "Geo|The node is currently %{minutes_behind} behind the primary node."
msgstr ""
msgid "Geo|This is a primary node"
msgstr ""
msgid "Geo|Tracking database entry will be removed. Are you sure?"
msgstr ""
......@@ -9578,15 +9521,9 @@ msgstr ""
msgid "Geo|Tracking entry for upload (%{type}/%{id}) was successfully removed."
msgstr ""
msgid "Geo|URL"
msgstr ""
msgid "Geo|Unknown state"
msgstr ""
msgid "Geo|Verification capacity"
msgstr ""
msgid "Geo|Verification failed - %{error}"
msgstr ""
......@@ -20159,9 +20096,6 @@ msgstr ""
msgid "The unique identifier for the Geo node. Must match %{geoNodeName} if it is set in gitlab.rb, otherwise it must match %{externalUrl} with a trailing slash"
msgstr ""
msgid "The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url` with a trailing slash"
msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
......
......@@ -7,12 +7,12 @@ module QA
module Geo
module Nodes
class New < QA::Page::Base
view 'ee/app/views/admin/geo/nodes/_form.html.haml' do
view 'ee/app/assets/javascripts/geo_node_form/components/geo_node_form_core.vue' do
element :node_name_field
element :node_url_field
end
view 'ee/app/views/admin/geo/nodes/new.html.haml' do
view 'ee/app/assets/javascripts/geo_node_form/components/geo_node_form.vue' do
element :add_node_button
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