Commit 16c148ba authored by Nick Thomas's avatar Nick Thomas

Fix the lock-yourself-out prevention code and specs for Geo

(cherry picked from commit 1eeaf62b12ba76818e7e38932e5d612f69e59ad0)
parent 20e2d7eb
...@@ -23,6 +23,7 @@ class GeoNode < ActiveRecord::Base ...@@ -23,6 +23,7 @@ class GeoNode < ActiveRecord::Base
validates :encrypted_secret_access_key, presence: true validates :encrypted_secret_access_key, presence: true
validates :geo_node_key, presence: true, if: :secondary? validates :geo_node_key, presence: true, if: :secondary?
validate :check_not_adding_primary_as_secondary, if: :secondary?
after_initialize :build_dependents after_initialize :build_dependents
after_save :expire_cache! after_save :expire_cache!
...@@ -216,12 +217,12 @@ class GeoNode < ActiveRecord::Base ...@@ -216,12 +217,12 @@ class GeoNode < ActiveRecord::Base
end end
end end
def validate(record) # Prevent locking yourself out
# Prevent locking yourself out def check_not_adding_primary_as_secondary
if record.host == Gitlab.config.gitlab.host && if host == Gitlab.config.gitlab.host &&
record.port == Gitlab.config.gitlab.port && port == Gitlab.config.gitlab.port &&
record.relative_url_root == Gitlab.config.gitlab.relative_url_root && !record.primary relative_url_root == Gitlab.config.gitlab.relative_url_root
record.errors[:base] << 'Current node must be the primary node or you will be locking yourself out' errors.add(:base, 'Current node must be the primary node or you will be locking yourself out')
end end
end end
......
---
title: Fix a Geo node validation, preventing admins from locking themselves out
merge_request: 3040
author:
type: fixed
...@@ -9,7 +9,6 @@ feature 'Geo clone instructions', :js do ...@@ -9,7 +9,6 @@ feature 'Geo clone instructions', :js do
background do background do
primary = create(:geo_node, :primary, schema: 'https', host: 'primary.domain.com', port: 443) primary = create(:geo_node, :primary, schema: 'https', host: 'primary.domain.com', port: 443)
primary.update_attribute(:clone_url_prefix, 'git@primary.domain.com:') primary.update_attribute(:clone_url_prefix, 'git@primary.domain.com:')
create(:geo_node, :current)
allow(Gitlab::Geo).to receive(:secondary?).and_return(true) allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
project.team << [developer, :developer] project.team << [developer, :developer]
......
...@@ -8,7 +8,7 @@ describe RemoveSystemHookFromGeoNodes, :migration do ...@@ -8,7 +8,7 @@ describe RemoveSystemHookFromGeoNodes, :migration do
allow_any_instance_of(WebHookService).to receive(:execute) allow_any_instance_of(WebHookService).to receive(:execute)
create(:system_hook) create(:system_hook)
geo_nodes.create! attributes_for(:geo_node, :primary, :current) geo_nodes.create! attributes_for(:geo_node, :primary)
geo_nodes.create! attributes_for(:geo_node, system_hook_id: create(:system_hook).id) geo_nodes.create! attributes_for(:geo_node, system_hook_id: create(:system_hook).id)
end end
......
...@@ -67,7 +67,7 @@ describe Namespace do ...@@ -67,7 +67,7 @@ describe Namespace do
describe '#move_dir' do describe '#move_dir' do
context 'when running on a primary node' do context 'when running on a primary node' do
let!(:geo_node) { create(:geo_node, :primary, :current) } let!(:geo_node) { create(:geo_node, :primary) }
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
it 'logs the Geo::RepositoryRenamedEvent for each project inside namespace' do it 'logs the Geo::RepositoryRenamedEvent for each project inside namespace' do
......
...@@ -749,7 +749,7 @@ describe Project do ...@@ -749,7 +749,7 @@ describe Project do
describe '#rename_repo' do describe '#rename_repo' do
context 'when running on a primary node' do context 'when running on a primary node' do
let!(:geo_node) { create(:geo_node, :primary, :current) } let!(:geo_node) { create(:geo_node, :primary) }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:gitlab_shell) { Gitlab::Shell.new } let(:gitlab_shell) { Gitlab::Shell.new }
......
...@@ -161,7 +161,7 @@ describe Projects::CreateService, '#execute' do ...@@ -161,7 +161,7 @@ describe Projects::CreateService, '#execute' do
end end
context 'when running on a primary node' do context 'when running on a primary node' do
let!(:geo_node) { create(:geo_node, :primary, :current) } let!(:geo_node) { create(:geo_node, :primary) }
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
expect { create_project(user, opts) }.to change(Geo::RepositoryCreatedEvent, :count).by(1) expect { create_project(user, opts) }.to change(Geo::RepositoryCreatedEvent, :count).by(1)
......
...@@ -31,7 +31,7 @@ describe Projects::DestroyService do ...@@ -31,7 +31,7 @@ describe Projects::DestroyService do
end end
context 'when running on a primary node' do context 'when running on a primary node' do
let!(:geo_node) { create(:geo_node, :primary, :current) } let!(:geo_node) { create(:geo_node, :primary) }
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
# Run Sidekiq immediately to check that renamed repository will be removed # Run Sidekiq immediately to check that renamed repository will be removed
......
...@@ -12,7 +12,7 @@ describe Projects::TransferService do ...@@ -12,7 +12,7 @@ describe Projects::TransferService do
end end
context 'when running on a primary node' do context 'when running on a primary node' do
let!(:geo_node) { create(:geo_node, :primary, :current) } let!(:geo_node) { create(:geo_node, :primary) }
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
expect { subject.execute(group) }.to change(Geo::RepositoryRenamedEvent, :count).by(1) expect { subject.execute(group) }.to change(Geo::RepositoryRenamedEvent, :count).by(1)
......
...@@ -9,9 +9,5 @@ FactoryGirl.define do ...@@ -9,9 +9,5 @@ FactoryGirl.define do
port { Gitlab.config.gitlab.port } port { Gitlab.config.gitlab.port }
geo_node_key nil geo_node_key nil
end end
trait :current do
port { Gitlab.config.gitlab.port }
end
end end
end end
require 'spec_helper' require 'spec_helper'
describe Gitlab::Geo::HealthCheck, :postgresql do describe Gitlab::Geo::HealthCheck, :postgresql do
let!(:secondary) { create(:geo_node, :current) } set(:secondary) { create(:geo_node) }
subject { described_class } subject { described_class }
before do
allow(Gitlab::Geo).to receive(:current_node).and_return(secondary)
end
describe '.perform_checks' do describe '.perform_checks' do
it 'returns a string if database is not fully migrated' do it 'returns a string if database is not fully migrated' do
allow(Gitlab::Geo).to receive(:secondary?) { true }
allow(described_class).to receive(:geo_database_configured?).and_return(true) allow(described_class).to receive(:geo_database_configured?).and_return(true)
allow(described_class).to receive(:database_secondary?).and_return(true) allow(described_class).to receive(:database_secondary?).and_return(true)
allow(described_class).to receive(:get_database_version).and_return('20170101') allow(described_class).to receive(:get_database_version).and_return('20170101')
...@@ -27,7 +30,6 @@ describe Gitlab::Geo::HealthCheck, :postgresql do ...@@ -27,7 +30,6 @@ describe Gitlab::Geo::HealthCheck, :postgresql do
end end
it 'returns an error when database is not configured for streaming replication' do it 'returns an error when database is not configured for streaming replication' do
allow(Gitlab::Geo).to receive(:secondary?) { true }
allow(Gitlab::Geo).to receive(:configured?) { true } allow(Gitlab::Geo).to receive(:configured?) { true }
allow(Gitlab::Database).to receive(:postgresql?) { true } allow(Gitlab::Database).to receive(:postgresql?) { true }
allow(described_class).to receive(:database_secondary?) { false } allow(described_class).to receive(:database_secondary?) { false }
...@@ -36,7 +38,6 @@ describe Gitlab::Geo::HealthCheck, :postgresql do ...@@ -36,7 +38,6 @@ describe Gitlab::Geo::HealthCheck, :postgresql do
end end
it 'returns an error when streaming replication is not working' do it 'returns an error when streaming replication is not working' do
allow(Gitlab::Geo).to receive(:secondary?) { true }
allow(Gitlab::Geo).to receive(:configured?) { true } allow(Gitlab::Geo).to receive(:configured?) { true }
allow(Gitlab::Database).to receive(:postgresql?) { true } allow(Gitlab::Database).to receive(:postgresql?) { true }
allow(described_class).to receive(:database_secondary?) { false } allow(described_class).to receive(:database_secondary?) { false }
......
...@@ -2,7 +2,11 @@ require 'spec_helper' ...@@ -2,7 +2,11 @@ require 'spec_helper'
describe Gitlab::Geo::LogCursor::Daemon, :postgresql do describe Gitlab::Geo::LogCursor::Daemon, :postgresql do
describe '#run!' do describe '#run!' do
let!(:geo_node) { create(:geo_node, :current) } set(:geo_node) { create(:geo_node) }
before do
allow(Gitlab::Geo).to receive(:current_node).and_return(geo_node)
end
it 'traps signals' do it 'traps signals' do
allow(subject).to receive(:exit?) { true } allow(subject).to receive(:exit?) { true }
...@@ -125,8 +129,6 @@ describe Gitlab::Geo::LogCursor::Daemon, :postgresql do ...@@ -125,8 +129,6 @@ describe Gitlab::Geo::LogCursor::Daemon, :postgresql do
end end
it 'schedules a GeoRepositoryDestroyWorker when event node is the current node' do it 'schedules a GeoRepositoryDestroyWorker when event node is the current node' do
allow(Gitlab::Geo).to receive(:current_node).and_return(geo_node)
expect(Geo::RepositoriesCleanUpWorker).to receive(:perform_in).with(within(5.minutes).of(1.hour), geo_node.id) expect(Geo::RepositoriesCleanUpWorker).to receive(:perform_in).with(within(5.minutes).of(1.hour), geo_node.id)
subject.run! subject.run!
...@@ -142,7 +144,6 @@ describe Gitlab::Geo::LogCursor::Daemon, :postgresql do ...@@ -142,7 +144,6 @@ describe Gitlab::Geo::LogCursor::Daemon, :postgresql do
end end
context 'when node has namespace restrictions' do context 'when node has namespace restrictions' do
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) }
let(:project) { create(:project, group: group_1) } let(:project) { create(:project, group: group_1) }
......
...@@ -53,14 +53,17 @@ describe GeoNode, type: :model do ...@@ -53,14 +53,17 @@ describe GeoNode, type: :model do
end end
context 'prevent locking yourself out' do context 'prevent locking yourself out' do
subject do
GeoNode.new(host: Gitlab.config.gitlab.host,
port: Gitlab.config.gitlab.port,
relative_url_root: Gitlab.config.gitlab.relative_url_root)
end
it 'does not accept adding a non primary node with same details as current_node' do it 'does not accept adding a non primary node with same details as current_node' do
expect(subject).not_to be_valid node = GeoNode.new(
host: Gitlab.config.gitlab.host,
port: Gitlab.config.gitlab.port,
relative_url_root: Gitlab.config.gitlab.relative_url_root,
geo_node_key: build(:geo_node_key)
)
expect(node).not_to be_valid
expect(node.errors.full_messages.count).to eq(1)
expect(node.errors[:base].first).to match('locking yourself out')
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe GeoNodeStatus do describe GeoNodeStatus do
let!(:geo_node) { create(:geo_node, :current) } set(:geo_node) { create(:geo_node, :primary) }
let(:group) { create(:group) } set(:group) { create(:group) }
let!(:project_1) { create(:project, group: group) } set(:project_1) { create(:project, group: group) }
let!(:project_2) { create(:project, group: group) } set(:project_2) { create(:project, group: group) }
let!(:project_3) { create(:project) } set(:project_3) { create(:project) }
let!(:project_4) { create(:project) } set(:project_4) { create(:project) }
subject { described_class.new } subject { described_class.new }
...@@ -99,7 +99,8 @@ describe GeoNodeStatus do ...@@ -99,7 +99,8 @@ describe GeoNodeStatus do
end end
describe '#db_replication_lag' do describe '#db_replication_lag' do
it 'returns the set replication lag' do it 'returns the set replication lag if secondary' do
allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
allow(Gitlab::Geo::HealthCheck).to receive(:db_replication_lag).and_return(1000) allow(Gitlab::Geo::HealthCheck).to receive(:db_replication_lag).and_return(1000)
expect(subject.db_replication_lag).to eq(1000) expect(subject.db_replication_lag).to eq(1000)
...@@ -107,7 +108,6 @@ describe GeoNodeStatus do ...@@ -107,7 +108,6 @@ describe GeoNodeStatus do
it "doesn't attempt to set replication lag if primary" do it "doesn't attempt to set replication lag if primary" do
expect(Gitlab::Geo::HealthCheck).not_to receive(:db_replication_lag) expect(Gitlab::Geo::HealthCheck).not_to receive(:db_replication_lag)
expect(Gitlab::Geo).to receive(:secondary?).and_return(false)
expect(subject.db_replication_lag).to eq(nil) expect(subject.db_replication_lag).to eq(nil)
end end
...@@ -198,7 +198,8 @@ describe GeoNodeStatus do ...@@ -198,7 +198,8 @@ describe GeoNodeStatus do
expect(subject.cursor_last_event_date).to be_nil expect(subject.cursor_last_event_date).to be_nil
end end
it 'returns the latest event ID' do it 'returns the latest event ID if secondary' do
allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
event = create(:geo_event_log_state) event = create(:geo_event_log_state)
expect(subject.cursor_last_event_id).to eq(event.event_id) expect(subject.cursor_last_event_id).to eq(event.event_id)
...@@ -206,7 +207,6 @@ describe GeoNodeStatus do ...@@ -206,7 +207,6 @@ describe GeoNodeStatus do
it "doesn't attempt to retrieve cursor if primary" do it "doesn't attempt to retrieve cursor if primary" do
create(:geo_event_log_state) create(:geo_event_log_state)
expect(Gitlab::Geo).to receive(:secondary?).exactly(2).times.and_return(false)
expect(subject.cursor_last_event_date).to eq(nil) expect(subject.cursor_last_event_date).to eq(nil)
expect(subject.cursor_last_event_id).to eq(nil) expect(subject.cursor_last_event_id).to eq(nil)
......
require 'spec_helper' require 'spec_helper'
describe Geo::FileUploadService do describe Geo::FileUploadService do
let!(:node) { create(:geo_node, :current) } set(:node) { create(:geo_node, :primary) }
describe '#execute' do describe '#execute' do
context 'user avatar' do context 'user avatar' do
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Geo::NodeUpdateService do describe Geo::NodeUpdateService do
let(:group) { create(:group) } let(:group) { create(:group) }
let!(:primary) { create(:geo_node, :primary, :current) } 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: [group.id]) }
......
require 'spec_helper' require 'spec_helper'
describe Geo::FileDownloadDispatchWorker do describe Geo::FileDownloadDispatchWorker do
let!(:primary) { create(:geo_node, :primary, host: 'primary-geo-node') } set(:primary) { create(:geo_node, :primary, host: 'primary-geo-node') }
let!(:secondary) { create(:geo_node, :current) } set(:secondary) { create(:geo_node) }
before do before do
allow(Gitlab::Geo).to receive(:secondary?).and_return(true) allow(Gitlab::Geo).to receive(:current_node).and_return(secondary)
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(true) allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(true)
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:renew).and_return(true) allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:renew).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)
......
require 'spec_helper' require 'spec_helper'
describe Geo::RepositorySyncWorker, :postgresql do describe Geo::RepositorySyncWorker, :postgresql do
let!(:primary) { create(:geo_node, :primary, host: 'primary-geo-node') } set(:primary) { create(:geo_node, :primary, host: 'primary-geo-node') }
let!(:secondary) { create(:geo_node, :current) } set(:secondary) { create(:geo_node) }
let(:synced_group) { create(:group) } set(:synced_group) { create(:group) }
let!(:project_in_synced_group) { create(:project, group: synced_group) } set(:project_in_synced_group) { create(:project, group: synced_group) }
let!(:unsynced_project) { create(:project) } set(:unsynced_project) { create(:project) }
subject { described_class.new } subject { described_class.new }
before do
allow(Gitlab::Geo).to receive(:current_node).and_return(secondary)
end
describe '#perform' do describe '#perform' do
before do before do
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) { true } allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) { true }
......
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