Allow admins to select user namespaces to replicate

parent 00adcb32
......@@ -5,8 +5,8 @@ class GeoNode < ActiveRecord::Base
belongs_to :oauth_application, class_name: 'Doorkeeper::Application', dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent
belongs_to :system_hook, dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent
has_many :geo_node_group_links
has_many :groups, through: :geo_node_group_links
has_many :geo_node_namespace_links
has_many :namespaces, through: :geo_node_namespace_links
default_values schema: lambda { Gitlab.config.gitlab.protocol },
host: lambda { Gitlab.config.gitlab.host },
......@@ -109,9 +109,9 @@ class GeoNode < ActiveRecord::Base
end
def project_ids
return unless groups.any?
return unless namespaces.any?
groups.flat_map { |group| group.all_projects.pluck(:id) }.uniq
namespaces.flat_map { |namespace| namespace.all_projects.pluck(:id) }.uniq
end
private
......
class GeoNodeGroupLink < ActiveRecord::Base
belongs_to :geo_node
belongs_to :group
validates :geo_node_id, :group_id, presence: true
validates :group_id, uniqueness: { scope: [:geo_node_id] }
end
class GeoNodeNamespaceLink < ActiveRecord::Base
belongs_to :geo_node, inverse_of: :namespaces
belongs_to :namespace
validates :namespace_id, presence: true
validates :namespace_id, uniqueness: { scope: [:geo_node_id] }
end
......@@ -122,7 +122,7 @@ class GeoNodeStatus
@attachments ||=
if restricted_project_ids
uploads_table = Upload.arel_table
group_uploads = uploads_table[:model_type].eq('Namespace').and(uploads_table[:model_id].in(Gitlab::Geo.current_node.group_ids))
group_uploads = uploads_table[:model_type].eq('Namespace').and(uploads_table[:model_id].in(Gitlab::Geo.current_node.namespace_ids))
project_uploads = uploads_table[:model_type].eq('Project').and(uploads_table[:model_id].in(restricted_project_ids))
other_uploads = uploads_table[:model_type].not_in(%w[Namespace Project])
......
......@@ -22,10 +22,9 @@
Paste a machine public key here for the GitLab user this node runs on. Read more about how to generate it
= link_to "here", help_page_path("ssh/README")
- if geo_node.persisted? && geo_node.secondary?
.form-group
= form.label :group_ids, 'Groups to replicate', class: 'control-label'
.col-sm-10
= form.select :group_ids, namespaces_options(geo_node.group_ids), { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'group_ids' }
.help-block
Choose which groups you wish to replicate.
.form-group
= form.label :namespace_ids, 'Namespaces to replicate', class: 'control-label'
.col-sm-10
= form.select :namespace_ids, namespaces_options(geo_node.namespace_ids), { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'group_ids' }
.help-block
Choose which namespaces you wish to replicate. Left in blank to replicate all.
......@@ -33,12 +33,12 @@
%span.help-block Primary node
- else
= status_loading_icon
- if node.groups.any?
- if node.namespaces.any?
%p
%span.help-block
Groups to replicate:
Namespaces to replicate:
%strong.node-info
= node_selected_groups_to_replicate(node)
= node_selected_namespaces_to_replicate(node)
.js-geo-node-status{ style: 'display: none' }
- if node.enabled?
%p
......
......@@ -22,7 +22,7 @@ module Geo
relation =
if restricted_project_ids
uploads_table = Upload.arel_table
group_uploads = uploads_table[:model_type].eq('Namespace').and(uploads_table[:model_id].in(Gitlab::Geo.current_node.group_ids))
group_uploads = uploads_table[:model_type].eq('Namespace').and(uploads_table[:model_id].in(Gitlab::Geo.current_node.namespace_ids))
project_uploads = uploads_table[:model_type].eq('Project').and(uploads_table[:model_id].in(restricted_project_ids))
other_uploads = uploads_table[:model_type].not_in(%w[Namespace Project])
......
class CreateGeoNodeGroupLinks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
create_table :geo_node_group_links do |t|
t.references :geo_node, index: true, foreign_key: { on_delete: :cascade }, null: false
t.integer :group_id, null: false
end
add_timestamps_with_timezone :geo_node_group_links
add_concurrent_foreign_key :geo_node_group_links, :namespaces, column: :group_id, on_delete: :cascade
add_concurrent_index :geo_node_group_links, [:geo_node_id, :group_id], unique: true
end
def down
remove_foreign_key :geo_node_group_links, column: :group_id
if index_exists?(:geo_node_group_links, [:geo_node_id, :group_id])
remove_concurrent_index :geo_node_group_links, [:geo_node_id, :group_id]
end
drop_table :geo_node_group_links
end
end
class CreateGeoNodeNamespaceLinks < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
create_table :geo_node_namespace_links do |t|
t.references :geo_node, index: true, foreign_key: { on_delete: :cascade }, null: false
t.integer :namespace_id, null: false
end
add_timestamps_with_timezone :geo_node_namespace_links
add_concurrent_foreign_key :geo_node_namespace_links, :namespaces, column: :namespace_id, on_delete: :cascade
add_concurrent_index :geo_node_namespace_links, [:geo_node_id, :namespace_id], unique: true
end
def down
remove_foreign_key :geo_node_namespace_links, column: :namespace_id
if index_exists?(:geo_node_namespace_links, [:geo_node_id, :namespace_id])
remove_concurrent_index :geo_node_namespace_links, [:geo_node_id, :namespace_id]
end
drop_table :geo_node_namespace_links
end
end
......@@ -633,15 +633,15 @@ ActiveRecord::Schema.define(version: 20170803130232) do
add_index "geo_event_log", ["repository_updated_event_id"], name: "index_geo_event_log_on_repository_updated_event_id", using: :btree
create_table "geo_node_group_links", force: :cascade do |t|
create_table "geo_node_namespace_links", force: :cascade do |t|
t.integer "geo_node_id", null: false
t.integer "group_id", null: false
t.integer "namespace_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "geo_node_group_links", ["geo_node_id", "group_id"], name: "index_geo_node_group_links_on_geo_node_id_and_group_id", unique: true, using: :btree
add_index "geo_node_group_links", ["geo_node_id"], name: "index_geo_node_group_links_on_geo_node_id", using: :btree
add_index "geo_node_namespace_links", ["geo_node_id", "namespace_id"], name: "index_geo_node_namespace_links_on_geo_node_id_and_namespace_id", unique: true, using: :btree
add_index "geo_node_namespace_links", ["geo_node_id"], name: "index_geo_node_namespace_links_on_geo_node_id", using: :btree
create_table "geo_nodes", force: :cascade do |t|
t.string "schema"
......@@ -1984,8 +1984,8 @@ ActiveRecord::Schema.define(version: 20170803130232) do
add_foreign_key "geo_event_log", "geo_repository_deleted_events", column: "repository_deleted_event_id", name: "fk_c4b1c1f66e", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_repository_renamed_events", column: "repository_renamed_event_id", name: "fk_86c84214ec", on_delete: :cascade
add_foreign_key "geo_event_log", "geo_repository_updated_events", column: "repository_updated_event_id", on_delete: :cascade
add_foreign_key "geo_node_group_links", "geo_nodes", on_delete: :cascade
add_foreign_key "geo_node_group_links", "namespaces", column: "group_id", name: "fk_e684c3550a", on_delete: :cascade
add_foreign_key "geo_node_namespace_links", "geo_nodes", on_delete: :cascade
add_foreign_key "geo_node_namespace_links", "namespaces", name: "fk_41ff5fb854", on_delete: :cascade
add_foreign_key "geo_repository_renamed_events", "projects", on_delete: :cascade
add_foreign_key "geo_repository_updated_events", "projects", on_delete: :cascade
add_foreign_key "index_statuses", "projects", name: "fk_74b2492545", on_delete: :cascade
......
......@@ -79,7 +79,7 @@ class Admin::GeoNodesController < Admin::ApplicationController
private
def geo_node_params
params.require(:geo_node).permit(:url, :primary, group_ids: [], geo_node_key_attributes: [:key])
params.require(:geo_node).permit(:url, :primary, namespace_ids: [], geo_node_key_attributes: [:key])
end
def check_license
......
module EE
module GeoHelper
def node_selected_groups_to_replicate(node)
node.groups.sort_by(&:human_name).map(&:human_name).join(', ')
def node_selected_namespaces_to_replicate(node)
node.namespaces.sort_by(&:human_name).map(&:human_name).join(', ')
end
def node_status_icon(node)
......
FactoryGirl.define do
factory :geo_node_group_link do
factory :geo_node_namespace_link do
geo_node
group
namespace
end
end
......@@ -81,7 +81,7 @@ describe Gitlab::Geo::LogCursor::Daemon do
end
end
context 'when node have group restrictions' do
context 'when node have namespace restrictions' do
let(:geo_node) { create(:geo_node, :current) }
let(:group_1) { create(:group) }
let(:group_2) { create(:group) }
......@@ -94,14 +94,14 @@ describe Gitlab::Geo::LogCursor::Daemon do
allow(subject).to receive(:exit?).and_return(false, true)
end
it 'replays events for projects that belong to selected groups to replicate' do
geo_node.update_attribute(:groups, [group_1])
it 'replays events for projects that belong to selected namespaces to replicate' do
geo_node.update_attribute(:namespaces, [group_1])
expect { subject.run! }.to change(Geo::ProjectRegistry, :count).by(1)
end
it 'does not replay events for projects that do not belong to selected groups to replicate' do
geo_node.update_attribute(:groups, [group_2])
it 'does not replay events for projects that do not belong to selected namespaces to replicate' do
geo_node.update_attribute(:namespaces, [group_2])
expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count)
end
......@@ -109,14 +109,14 @@ describe Gitlab::Geo::LogCursor::Daemon do
context 'when performing a full scan' do
subject { described_class.new(full_scan: true) }
it 'creates registries for missing projects that belong to selected groups' do
geo_node.update_attribute(:groups, [group_1])
it 'creates registries for missing projects that belong to selected namespaces' do
geo_node.update_attribute(:namespaces, [group_1])
expect { subject.run! }.to change(Geo::ProjectRegistry, :count).by(1)
end
it 'does not create registries for missing projects that do not belong to selected groups' do
geo_node.update_attribute(:groups, [group_2])
it 'does not create registries for missing projects that do not belong to selected namespaces' do
geo_node.update_attribute(:namespaces, [group_2])
expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count)
end
......
require 'spec_helper'
describe GeoNodeGroupLink, models: true do
describe 'relationships' do
it { is_expected.to belong_to(:geo_node) }
it { is_expected.to belong_to(:group) }
end
describe 'validations' do
let!(:geo_node_group_link) { create(:geo_node_group_link) }
it { is_expected.to validate_presence_of(:geo_node_id) }
it { is_expected.to validate_presence_of(:group_id) }
it { is_expected.to validate_uniqueness_of(:group_id).scoped_to(:geo_node_id) }
end
end
require 'spec_helper'
describe GeoNodeNamespaceLink, models: true do
describe 'relationships' do
it { is_expected.to belong_to(:geo_node) }
it { is_expected.to belong_to(:namespace) }
end
describe 'validations' do
let!(:geo_node_namespace_link) { create(:geo_node_namespace_link) }
it { is_expected.to validate_presence_of(:namespace_id) }
it { is_expected.to validate_uniqueness_of(:namespace_id).scoped_to(:geo_node_id) }
end
end
......@@ -15,8 +15,8 @@ describe GeoNode, type: :model do
it { is_expected.to belong_to(:geo_node_key).dependent(:destroy) }
it { is_expected.to belong_to(:oauth_application).dependent(:destroy) }
it { is_expected.to have_many(:geo_node_group_links) }
it { is_expected.to have_many(:groups).through(:geo_node_group_links) }
it { is_expected.to have_many(:geo_node_namespace_links) }
it { is_expected.to have_many(:namespaces).through(:geo_node_namespace_links) }
end
context 'default values' do
......@@ -333,7 +333,7 @@ describe GeoNode, type: :model do
project_2 = create(:empty_project, group: nested_group_1)
project_3 = create(:empty_project, group: group_2)
node.update_attribute(:groups, [group_1, group_2, nested_group_1])
node.update_attribute(:namespaces, [group_1, group_2, nested_group_1])
expect(node.project_ids).to match_array([project_1.id, project_2.id, project_3.id])
end
......
......@@ -90,7 +90,7 @@ describe GeoNodeStatus do
end
it 'returns the right percentage with group restrictions' do
geo_node.update_attribute(:groups, [group])
geo_node.update_attribute(:namespaces, [group])
create(:geo_file_registry, :avatar, file_id: upload_1.id)
create(:geo_file_registry, :avatar, file_id: upload_2.id)
......@@ -119,7 +119,7 @@ describe GeoNodeStatus do
end
it 'returns the right percentage with group restrictions' do
geo_node.update_attribute(:groups, [group])
geo_node.update_attribute(:namespaces, [group])
create(:geo_file_registry, :lfs, file_id: lfs_object_project.lfs_object_id)
expect(subject.lfs_objects_synced_in_percentage).to be_within(0.0001).of(50)
......@@ -137,7 +137,7 @@ describe GeoNodeStatus do
end
it 'returns the right number of failed repos with group restrictions' do
geo_node.update_attribute(:groups, [group])
geo_node.update_attribute(:namespaces, [group])
expect(subject.repositories_failed_count).to eq(1)
end
......@@ -155,7 +155,7 @@ describe GeoNodeStatus do
end
it 'returns the right percentage with group restrictions' do
geo_node.update_attribute(:groups, [group])
geo_node.update_attribute(:namespaces, [group])
create(:geo_project_registry, :synced, project: project_1)
expect(subject.repositories_synced_in_percentage).to be_within(0.0001).of(50)
......
......@@ -75,7 +75,7 @@ describe Geo::FileDownloadDispatchWorker do
end
end
context 'when node have group restrictions' do
context 'when node have namespace restrictions' do
let(:group_1) { create(:group) }
let!(:project_1) { create(:empty_project, group: group_1) }
let!(:project_2) { create(:empty_project) }
......@@ -84,10 +84,10 @@ describe Geo::FileDownloadDispatchWorker do
allow(ProjectCacheWorker).to receive(:perform_async).and_return(true)
allow_any_instance_of(described_class).to receive(:over_time?).and_return(false)
secondary.update_attribute(:groups, [group_1])
secondary.update_attribute(:namespaces, [group_1])
end
it 'does not perform GeoFileDownloadWorker for LFS object that do not belong to selected groups to replicate' do
it 'does not perform GeoFileDownloadWorker for LFS object that do not belong to selected namespaces to replicate' do
create(:lfs_objects_project, project: project_1)
create(:lfs_objects_project, project: project_2)
......@@ -96,7 +96,7 @@ describe Geo::FileDownloadDispatchWorker do
subject.perform
end
it 'does not perform GeoFileDownloadWorker for upload objects that do not belong to selected groups to replicate' do
it 'does not perform GeoFileDownloadWorker for upload objects that do not belong to selected namespaces to replicate' do
avatar = fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'))
create(:upload, model: group_1, path: avatar)
create(:upload, model: create(:group), path: avatar)
......
......@@ -64,18 +64,18 @@ describe Geo::RepositorySyncWorker do
subject.perform
end
context 'when node have group restrictions' do
context 'when node have namespace restrictions' do
before do
secondary.update_attribute(:groups, [group])
secondary.update_attribute(:namespaces, [group])
end
it 'does not perform Geo::ProjectSyncWorker for projects that do not belong to selected groups to replicate' do
it 'does not perform Geo::ProjectSyncWorker for projects that do not belong to selected namespaces to replicate' do
expect(Geo::ProjectSyncWorker).to receive(:perform_in).once.and_return(spy)
subject.perform
end
it 'does not perform Geo::ProjectSyncWorker for synced projects updated recently that do not belong to selected groups to replicate' do
it 'does not perform Geo::ProjectSyncWorker for synced projects updated recently that do not belong to selected namespaces to replicate' do
create(:geo_project_registry, :synced, :repository_dirty, project: project_1)
create(:geo_project_registry, :synced, :repository_dirty, project: project_2)
......
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