Allow admins to select user namespaces to replicate

parent 00adcb32
...@@ -5,8 +5,8 @@ class GeoNode < ActiveRecord::Base ...@@ -5,8 +5,8 @@ class GeoNode < ActiveRecord::Base
belongs_to :oauth_application, class_name: 'Doorkeeper::Application', dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent belongs_to :oauth_application, class_name: 'Doorkeeper::Application', dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent
belongs_to :system_hook, dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent belongs_to :system_hook, dependent: :destroy # rubocop: disable Cop/ActiveRecordDependent
has_many :geo_node_group_links has_many :geo_node_namespace_links
has_many :groups, through: :geo_node_group_links has_many :namespaces, through: :geo_node_namespace_links
default_values schema: lambda { Gitlab.config.gitlab.protocol }, default_values schema: lambda { Gitlab.config.gitlab.protocol },
host: lambda { Gitlab.config.gitlab.host }, host: lambda { Gitlab.config.gitlab.host },
...@@ -109,9 +109,9 @@ class GeoNode < ActiveRecord::Base ...@@ -109,9 +109,9 @@ class GeoNode < ActiveRecord::Base
end end
def project_ids 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 end
private 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 ...@@ -122,7 +122,7 @@ class GeoNodeStatus
@attachments ||= @attachments ||=
if restricted_project_ids if restricted_project_ids
uploads_table = Upload.arel_table 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)) 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]) other_uploads = uploads_table[:model_type].not_in(%w[Namespace Project])
......
...@@ -22,10 +22,9 @@ ...@@ -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 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") = link_to "here", help_page_path("ssh/README")
- if geo_node.persisted? && geo_node.secondary? .form-group
.form-group = form.label :namespace_ids, 'Namespaces to replicate', class: 'control-label'
= form.label :group_ids, 'Groups to replicate', class: 'control-label'
.col-sm-10 .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' } = 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 .help-block
Choose which groups you wish to replicate. Choose which namespaces you wish to replicate. Left in blank to replicate all.
...@@ -33,12 +33,12 @@ ...@@ -33,12 +33,12 @@
%span.help-block Primary node %span.help-block Primary node
- else - else
= status_loading_icon = status_loading_icon
- if node.groups.any? - if node.namespaces.any?
%p %p
%span.help-block %span.help-block
Groups to replicate: Namespaces to replicate:
%strong.node-info %strong.node-info
= node_selected_groups_to_replicate(node) = node_selected_namespaces_to_replicate(node)
.js-geo-node-status{ style: 'display: none' } .js-geo-node-status{ style: 'display: none' }
- if node.enabled? - if node.enabled?
%p %p
......
...@@ -22,7 +22,7 @@ module Geo ...@@ -22,7 +22,7 @@ module Geo
relation = relation =
if restricted_project_ids if restricted_project_ids
uploads_table = Upload.arel_table 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)) 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]) 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 ...@@ -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 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 "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 "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end 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_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_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"], name: "index_geo_node_namespace_links_on_geo_node_id", using: :btree
create_table "geo_nodes", force: :cascade do |t| create_table "geo_nodes", force: :cascade do |t|
t.string "schema" t.string "schema"
...@@ -1984,8 +1984,8 @@ ActiveRecord::Schema.define(version: 20170803130232) do ...@@ -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_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_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_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_namespace_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", "namespaces", name: "fk_41ff5fb854", on_delete: :cascade
add_foreign_key "geo_repository_renamed_events", "projects", 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 "geo_repository_updated_events", "projects", on_delete: :cascade
add_foreign_key "index_statuses", "projects", name: "fk_74b2492545", on_delete: :cascade add_foreign_key "index_statuses", "projects", name: "fk_74b2492545", on_delete: :cascade
......
...@@ -79,7 +79,7 @@ class Admin::GeoNodesController < Admin::ApplicationController ...@@ -79,7 +79,7 @@ class Admin::GeoNodesController < Admin::ApplicationController
private private
def geo_node_params 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 end
def check_license def check_license
......
module EE module EE
module GeoHelper module GeoHelper
def node_selected_groups_to_replicate(node) def node_selected_namespaces_to_replicate(node)
node.groups.sort_by(&:human_name).map(&:human_name).join(', ') node.namespaces.sort_by(&:human_name).map(&:human_name).join(', ')
end end
def node_status_icon(node) def node_status_icon(node)
......
FactoryGirl.define do FactoryGirl.define do
factory :geo_node_group_link do factory :geo_node_namespace_link do
geo_node geo_node
group namespace
end end
end end
...@@ -81,7 +81,7 @@ describe Gitlab::Geo::LogCursor::Daemon do ...@@ -81,7 +81,7 @@ describe Gitlab::Geo::LogCursor::Daemon do
end end
end end
context 'when node have group restrictions' do context 'when node have namespace restrictions' do
let(:geo_node) { create(:geo_node, :current) } let(:geo_node) { create(:geo_node, :current) }
let(:group_1) { create(:group) } let(:group_1) { create(:group) }
let(:group_2) { create(:group) } let(:group_2) { create(:group) }
...@@ -94,14 +94,14 @@ describe Gitlab::Geo::LogCursor::Daemon do ...@@ -94,14 +94,14 @@ describe Gitlab::Geo::LogCursor::Daemon do
allow(subject).to receive(:exit?).and_return(false, true) allow(subject).to receive(:exit?).and_return(false, true)
end end
it 'replays events for projects that belong to selected groups to replicate' do it 'replays events for projects that belong to selected namespaces to replicate' do
geo_node.update_attribute(:groups, [group_1]) geo_node.update_attribute(:namespaces, [group_1])
expect { subject.run! }.to change(Geo::ProjectRegistry, :count).by(1) expect { subject.run! }.to change(Geo::ProjectRegistry, :count).by(1)
end end
it 'does not replay events for projects that do not belong to selected groups to replicate' do it 'does not replay events for projects that do not belong to selected namespaces to replicate' do
geo_node.update_attribute(:groups, [group_2]) geo_node.update_attribute(:namespaces, [group_2])
expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count) expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count)
end end
...@@ -109,14 +109,14 @@ describe Gitlab::Geo::LogCursor::Daemon do ...@@ -109,14 +109,14 @@ describe Gitlab::Geo::LogCursor::Daemon do
context 'when performing a full scan' do context 'when performing a full scan' do
subject { described_class.new(full_scan: true) } subject { described_class.new(full_scan: true) }
it 'creates registries for missing projects that belong to selected groups' do it 'creates registries for missing projects that belong to selected namespaces' do
geo_node.update_attribute(:groups, [group_1]) geo_node.update_attribute(:namespaces, [group_1])
expect { subject.run! }.to change(Geo::ProjectRegistry, :count).by(1) expect { subject.run! }.to change(Geo::ProjectRegistry, :count).by(1)
end end
it 'does not create registries for missing projects that do not belong to selected groups' do it 'does not create registries for missing projects that do not belong to selected namespaces' do
geo_node.update_attribute(:groups, [group_2]) geo_node.update_attribute(:namespaces, [group_2])
expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count) expect { subject.run! }.not_to change(Geo::ProjectRegistry, :count)
end 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 ...@@ -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(:geo_node_key).dependent(:destroy) }
it { is_expected.to belong_to(:oauth_application).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(:geo_node_namespace_links) }
it { is_expected.to have_many(:groups).through(:geo_node_group_links) } it { is_expected.to have_many(:namespaces).through(:geo_node_namespace_links) }
end end
context 'default values' do context 'default values' do
...@@ -333,7 +333,7 @@ describe GeoNode, type: :model do ...@@ -333,7 +333,7 @@ describe GeoNode, type: :model do
project_2 = create(:empty_project, group: nested_group_1) project_2 = create(:empty_project, group: nested_group_1)
project_3 = create(:empty_project, group: group_2) 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]) expect(node.project_ids).to match_array([project_1.id, project_2.id, project_3.id])
end end
......
...@@ -90,7 +90,7 @@ describe GeoNodeStatus do ...@@ -90,7 +90,7 @@ describe GeoNodeStatus do
end end
it 'returns the right percentage with group restrictions' do 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_1.id)
create(:geo_file_registry, :avatar, file_id: upload_2.id) create(:geo_file_registry, :avatar, file_id: upload_2.id)
...@@ -119,7 +119,7 @@ describe GeoNodeStatus do ...@@ -119,7 +119,7 @@ describe GeoNodeStatus do
end end
it 'returns the right percentage with group restrictions' do 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) 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) expect(subject.lfs_objects_synced_in_percentage).to be_within(0.0001).of(50)
...@@ -137,7 +137,7 @@ describe GeoNodeStatus do ...@@ -137,7 +137,7 @@ describe GeoNodeStatus do
end end
it 'returns the right number of failed repos with group restrictions' do 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) expect(subject.repositories_failed_count).to eq(1)
end end
...@@ -155,7 +155,7 @@ describe GeoNodeStatus do ...@@ -155,7 +155,7 @@ describe GeoNodeStatus do
end end
it 'returns the right percentage with group restrictions' do 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) create(:geo_project_registry, :synced, project: project_1)
expect(subject.repositories_synced_in_percentage).to be_within(0.0001).of(50) expect(subject.repositories_synced_in_percentage).to be_within(0.0001).of(50)
......
...@@ -75,7 +75,7 @@ describe Geo::FileDownloadDispatchWorker do ...@@ -75,7 +75,7 @@ describe Geo::FileDownloadDispatchWorker do
end end
end end
context 'when node have group restrictions' do context 'when node have namespace restrictions' do
let(:group_1) { create(:group) } let(:group_1) { create(:group) }
let!(:project_1) { create(:empty_project, group: group_1) } let!(:project_1) { create(:empty_project, group: group_1) }
let!(:project_2) { create(:empty_project) } let!(:project_2) { create(:empty_project) }
...@@ -84,10 +84,10 @@ describe Geo::FileDownloadDispatchWorker do ...@@ -84,10 +84,10 @@ describe Geo::FileDownloadDispatchWorker do
allow(ProjectCacheWorker).to receive(:perform_async).and_return(true) allow(ProjectCacheWorker).to receive(:perform_async).and_return(true)
allow_any_instance_of(described_class).to receive(:over_time?).and_return(false) 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 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_1)
create(:lfs_objects_project, project: project_2) create(:lfs_objects_project, project: project_2)
...@@ -96,7 +96,7 @@ describe Geo::FileDownloadDispatchWorker do ...@@ -96,7 +96,7 @@ describe Geo::FileDownloadDispatchWorker do
subject.perform subject.perform
end 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')) avatar = fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'))
create(:upload, model: group_1, path: avatar) create(:upload, model: group_1, path: avatar)
create(:upload, model: create(:group), path: avatar) create(:upload, model: create(:group), path: avatar)
......
...@@ -64,18 +64,18 @@ describe Geo::RepositorySyncWorker do ...@@ -64,18 +64,18 @@ describe Geo::RepositorySyncWorker do
subject.perform subject.perform
end end
context 'when node have group restrictions' do context 'when node have namespace restrictions' do
before do before do
secondary.update_attribute(:groups, [group]) secondary.update_attribute(:namespaces, [group])
end 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) expect(Geo::ProjectSyncWorker).to receive(:perform_in).once.and_return(spy)
subject.perform subject.perform
end 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_1)
create(:geo_project_registry, :synced, :repository_dirty, project: project_2) 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