Commit d831dfdd authored by Tiago Botelho's avatar Tiago Botelho

Adds admin application setting to globably disable push mirrors.

parent 0bf399f5
...@@ -75,17 +75,36 @@ class Projects::MirrorsController < Projects::ApplicationController ...@@ -75,17 +75,36 @@ class Projects::MirrorsController < Projects::ApplicationController
@remote_mirror = @project.remote_mirrors.first_or_initialize @remote_mirror = @project.remote_mirrors.first_or_initialize
end end
def remote_mirror_attributes
{ remote_mirrors_attributes: %i[url id enabled] }
end
def mirror_params_attributes
attributes = [
:mirror,
:import_url,
:username_only_import_url,
:mirror_user_id,
:mirror_trigger_builds,
import_data_attributes: %i[
id
auth_method
password
ssh_known_hosts
regenerate_ssh_private_key
]
]
if can?(current_user, :admin_remote_mirror, project)
attributes << remote_mirror_attributes
end
attributes
end
def mirror_params def mirror_params
params.require(:project) params.require(:project).permit(mirror_params_attributes)
.permit(
:mirror,
:import_url,
:username_only_import_url,
:mirror_user_id,
:mirror_trigger_builds,
import_data_attributes: [:id, :auth_method, :password, :ssh_known_hosts, :regenerate_ssh_private_key],
remote_mirrors_attributes: [:url, :id, :enabled]
)
end end
def safe_mirror_params def safe_mirror_params
......
...@@ -17,6 +17,7 @@ class RemoteMirror < ActiveRecord::Base ...@@ -17,6 +17,7 @@ class RemoteMirror < ActiveRecord::Base
validate :url_availability, if: -> (mirror) { mirror.url_changed? || mirror.enabled? } validate :url_availability, if: -> (mirror) { mirror.url_changed? || mirror.enabled? }
after_save :set_override_remote_mirror_available, unless: -> { Gitlab::CurrentSettings.current_application_settings.remote_mirror_available }
after_save :refresh_remote, if: :mirror_url_changed? after_save :refresh_remote, if: :mirror_url_changed?
after_update :reset_fields, if: :mirror_url_changed? after_update :reset_fields, if: :mirror_url_changed?
after_destroy :remove_remote after_destroy :remove_remote
...@@ -85,6 +86,7 @@ class RemoteMirror < ActiveRecord::Base ...@@ -85,6 +86,7 @@ class RemoteMirror < ActiveRecord::Base
def enabled def enabled
return false unless project && super return false unless project && super
return false unless project.remote_mirror_available?
return false unless project.repository_exists? return false unless project.repository_exists?
return false if project.pending_delete? return false if project.pending_delete?
...@@ -147,6 +149,12 @@ class RemoteMirror < ActiveRecord::Base ...@@ -147,6 +149,12 @@ class RemoteMirror < ActiveRecord::Base
) )
end end
def set_override_remote_mirror_available
enabled = read_attribute(:enabled)
project.update(remote_mirror_available_overridden: enabled)
end
def refresh_remote def refresh_remote
return unless project return unless project
......
...@@ -126,7 +126,7 @@ class GitPushService < BaseService ...@@ -126,7 +126,7 @@ class GitPushService < BaseService
protected protected
def update_remote_mirrors def update_remote_mirrors
return if @project.remote_mirrors.empty? return unless @project.has_remote_mirror?
@project.mark_stuck_remote_mirrors_as_failed! @project.mark_stuck_remote_mirrors_as_failed!
@project.update_remote_mirrors @project.update_remote_mirrors
......
...@@ -126,7 +126,9 @@ ...@@ -126,7 +126,9 @@
Enabling this will only make licensed EE features available to projects if the project namespace's plan Enabling this will only make licensed EE features available to projects if the project namespace's plan
includes the feature or if the project is public. includes the feature or if the project is public.
= render partial: 'repository_mirrors_form', locals: { f: f } - if License.feature_available?(:repository_mirrors)
= render partial: 'repository_mirrors_form', locals: { f: f }
= render partial: 'repository_remote_mirrors_form', locals: { f: f }
%fieldset %fieldset
%legend Sign-up Restrictions %legend Sign-up Restrictions
......
%fieldset
%legend Repository Remote mirror settings
.form-group
= f.label :remote_mirror_available, 'Enable remote mirror configuration', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :remote_mirror_available do
= f.check_box :remote_mirror_available
Allow remote mirrors to be setup for projects
%span.help-block
If disabled, only admins will be able to setup remote mirrors in projects.
= link_to icon('question-circle'), help_page_path('workflow/repository_mirroring', anchor: 'pushing-to-a-remote-repository')
...@@ -3,4 +3,5 @@ ...@@ -3,4 +3,5 @@
- if @project.feature_available?(:repository_mirrors) - if @project.feature_available?(:repository_mirrors)
= render 'projects/mirrors/pull' = render 'projects/mirrors/pull'
= render 'projects/mirrors/push' - if can?(current_user, :admin_remote_mirror, @project)
= render 'projects/mirrors/push'
---
title: Allow admins to globally disable all remote mirrors from application settings
page.
merge_request:
author:
type: added
class AddRemoteMirrorAvailableToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:application_settings, :remote_mirror_available, :boolean, default: true, allow_null: false)
end
def down
remove_column(:application_settings, :remote_mirror_available)
end
end
class AddRemoteMirrorAvailableOverriddenToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column(:projects, :remote_mirror_available_overridden, :boolean)
end
def down
remove_column(:projects, :remote_mirror_available_overridden)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20171010140746) do ActiveRecord::Schema.define(version: 20171017130239) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -157,6 +157,7 @@ ActiveRecord::Schema.define(version: 20171010140746) do ...@@ -157,6 +157,7 @@ ActiveRecord::Schema.define(version: 20171010140746) do
t.boolean "hashed_storage_enabled", default: false, null: false t.boolean "hashed_storage_enabled", default: false, null: false
t.boolean "project_export_enabled", default: true, null: false t.boolean "project_export_enabled", default: true, null: false
t.boolean "auto_devops_enabled", default: false, null: false t.boolean "auto_devops_enabled", default: false, null: false
t.boolean "remote_mirror_available", default: true, null: false
end end
create_table "approvals", force: :cascade do |t| create_table "approvals", force: :cascade do |t|
...@@ -1603,6 +1604,7 @@ ActiveRecord::Schema.define(version: 20171010140746) do ...@@ -1603,6 +1604,7 @@ ActiveRecord::Schema.define(version: 20171010140746) do
t.boolean "disable_overriding_approvers_per_merge_request" t.boolean "disable_overriding_approvers_per_merge_request"
t.integer "storage_version", limit: 2 t.integer "storage_version", limit: 2
t.boolean "resolve_outdated_diff_discussions" t.boolean "resolve_outdated_diff_discussions"
t.boolean "remote_mirror_available_overridden"
end end
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
......
...@@ -22,7 +22,8 @@ module EE ...@@ -22,7 +22,8 @@ module EE
:slack_app_id, :slack_app_id,
:slack_app_secret, :slack_app_secret,
:slack_app_verification_token, :slack_app_verification_token,
:allow_group_owners_to_manage_ldap :allow_group_owners_to_manage_ldap,
:remote_mirror_available
] ]
end end
......
...@@ -40,7 +40,8 @@ module EE ...@@ -40,7 +40,8 @@ module EE
mirror_max_delay: Settings.gitlab['mirror_max_delay'], mirror_max_delay: Settings.gitlab['mirror_max_delay'],
mirror_max_capacity: Settings.gitlab['mirror_max_capacity'], mirror_max_capacity: Settings.gitlab['mirror_max_capacity'],
mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold'], mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold'],
allow_group_owners_to_manage_ldap: true allow_group_owners_to_manage_ldap: true,
remote_mirror_available: true
) )
end end
end end
......
...@@ -132,7 +132,9 @@ module EE ...@@ -132,7 +132,9 @@ module EE
end end
def has_remote_mirror? def has_remote_mirror?
feature_available?(:repository_mirrors) && remote_mirrors.enabled.exists? feature_available?(:repository_mirrors) &&
remote_mirror_available? &&
remote_mirrors.enabled.exists?
end end
def updating_remote_mirror? def updating_remote_mirror?
...@@ -140,7 +142,7 @@ module EE ...@@ -140,7 +142,7 @@ module EE
end end
def update_remote_mirrors def update_remote_mirrors
return unless feature_available?(:repository_mirrors) return unless feature_available?(:repository_mirrors) && remote_mirror_available?
remote_mirrors.enabled.each(&:sync) remote_mirrors.enabled.each(&:sync)
end end
...@@ -463,6 +465,11 @@ module EE ...@@ -463,6 +465,11 @@ module EE
@disabled_services @disabled_services
end end
def remote_mirror_available?
remote_mirror_available_overridden ||
current_application_settings.remote_mirror_available
end
private private
def licensed_feature_available?(feature) def licensed_feature_available?(feature)
......
...@@ -20,6 +20,11 @@ module EE ...@@ -20,6 +20,11 @@ module EE
!PushRule.global&.reject_unsigned_commits !PushRule.global&.reject_unsigned_commits
end end
with_scope :global
condition(:remote_mirror_available) do
::Gitlab::CurrentSettings.current_application_settings.remote_mirror_available
end
rule { admin }.enable :change_repository_storage rule { admin }.enable :change_repository_storage
rule { support_bot }.enable :guest_access rule { support_bot }.enable :guest_access
...@@ -49,6 +54,8 @@ module EE ...@@ -49,6 +54,8 @@ module EE
rule { can?(:developer_access) }.enable :admin_board rule { can?(:developer_access) }.enable :admin_board
rule { (remote_mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
rule { deploy_board_disabled & ~is_development }.prevent :read_deploy_board rule { deploy_board_disabled & ~is_development }.prevent :read_deploy_board
rule { can?(:master_access) }.policy do rule { can?(:master_access) }.policy do
......
...@@ -105,6 +105,7 @@ excluded_attributes: ...@@ -105,6 +105,7 @@ excluded_attributes:
- :mirror_user_id - :mirror_user_id
- :mirror_trigger_builds - :mirror_trigger_builds
- :storage_version - :storage_version
- :remote_mirror_available_overridden
snippets: snippets:
- :expired_at - :expired_at
merge_request_diff: merge_request_diff:
......
...@@ -4,6 +4,37 @@ describe Projects::MirrorsController do ...@@ -4,6 +4,37 @@ describe Projects::MirrorsController do
include ReactiveCachingHelpers include ReactiveCachingHelpers
describe 'setting up a remote mirror' do describe 'setting up a remote mirror' do
set(:project) { create(:project, :repository) }
let(:url) { 'http://foo.com' }
context 'when remote mirrors are disabled' do
before do
stub_application_setting(remote_mirror_available: false)
end
context 'when user is admin' do
let(:admin) { create(:user, :admin) }
it 'creates a new remote mirror' do
sign_in(admin)
expect do
do_put(project, remote_mirrors_attributes: { '0' => { 'enabled' => 1, 'url' => url } })
end.to change { RemoteMirror.count }.to(1)
end
end
context 'when user is not admin' do
it 'does not create a new remote mirror' do
sign_in(project.owner)
expect do
do_put(project, remote_mirrors_attributes: { '0' => { 'enabled' => 1, 'url' => url } })
end.not_to change { RemoteMirror.count }
end
end
end
context 'when the current project is a mirror' do context 'when the current project is a mirror' do
let(:project) { create(:project, :repository, :mirror) } let(:project) { create(:project, :repository, :mirror) }
...@@ -38,7 +69,6 @@ describe Projects::MirrorsController do ...@@ -38,7 +69,6 @@ describe Projects::MirrorsController do
context 'when the current project is not a mirror' do context 'when the current project is not a mirror' do
it 'allows to create a remote mirror' do it 'allows to create a remote mirror' do
project = create(:project, :repository)
sign_in(project.owner) sign_in(project.owner)
expect do expect do
...@@ -48,7 +78,6 @@ describe Projects::MirrorsController do ...@@ -48,7 +78,6 @@ describe Projects::MirrorsController do
end end
context 'when the current project has a remote mirror' do context 'when the current project has a remote mirror' do
let(:project) { create(:project, :repository) }
let(:remote_mirror) { project.remote_mirrors.create!(enabled: 1, url: 'http://local.dev') } let(:remote_mirror) { project.remote_mirrors.create!(enabled: 1, url: 'http://local.dev') }
before do before do
...@@ -117,7 +146,7 @@ describe Projects::MirrorsController do ...@@ -117,7 +146,7 @@ describe Projects::MirrorsController do
end end
end end
describe 'forcing an update' do describe 'forcing an update on a pull mirror' do
it 'forces update' do it 'forces update' do
expect_any_instance_of(EE::Project).to receive(:force_import_job!) expect_any_instance_of(EE::Project).to receive(:force_import_job!)
...@@ -128,6 +157,25 @@ describe Projects::MirrorsController do ...@@ -128,6 +157,25 @@ describe Projects::MirrorsController do
end end
end end
describe 'forcing an update on a push mirror' do
context 'when remote mirrors are disabled' do
let(:project) { create(:project, :repository, :remote_mirror) }
before do
stub_application_setting(remote_mirror_available: false)
sign_in(project.owner)
end
it 'updates now when overridden' do
project.update(remote_mirror_available_overridden: true)
expect_any_instance_of(EE::Project).to receive(:update_remote_mirrors)
put :update_now, { namespace_id: project.namespace.to_param, project_id: project.to_param, sync_remote: 1 }
end
end
end
describe '#update' do describe '#update' do
let(:project) { create(:project, :repository, :mirror, :remote_mirror) } let(:project) { create(:project, :repository, :mirror, :remote_mirror) }
......
...@@ -277,6 +277,15 @@ describe Project do ...@@ -277,6 +277,15 @@ describe Project do
update_remote_mirrors update_remote_mirrors
end end
it 'does nothing when remote mirror is disabled globally and not overridden' do
stub_application_setting(remote_mirror_available: false)
project.remote_mirror_available_overridden = false
expect_any_instance_of(RemoteMirror).not_to receive(:sync)
update_remote_mirrors
end
it 'does nothing when unlicensed' do it 'does nothing when unlicensed' do
stub_licensed_features(repository_mirrors: false) stub_licensed_features(repository_mirrors: false)
...@@ -824,6 +833,32 @@ describe Project do ...@@ -824,6 +833,32 @@ describe Project do
end end
end end
describe '#remote_mirror_available?' do
let(:project) { create(:project) }
context 'when remote mirror global setting is enabled' do
it 'returns true' do
expect(project.remote_mirror_available?).to be(true)
end
end
context 'when remote mirror global setting is disabled' do
before do
stub_application_setting(remote_mirror_available: false)
end
it 'returns true when overridden' do
project.remote_mirror_available_overridden = true
expect(project.remote_mirror_available?).to be(true)
end
it 'returns false when not overridden' do
expect(project.remote_mirror_available?).to be(false)
end
end
end
describe '#username_only_import_url' do describe '#username_only_import_url' do
where(:import_url, :username, :expected_import_url) do where(:import_url, :username, :expected_import_url) do
'' | 'foo' | '' '' | 'foo' | ''
......
require 'spec_helper'
describe ProjectPolicy do
set(:owner) { create(:user) }
set(:admin) { create(:admin) }
set(:developer) { create(:user) }
let(:project) { create(:project, :public, namespace: owner.namespace) }
before do
project.add_developer(developer)
end
context 'admin_remote_mirror' do
context 'with remote mirror setting enabled' do
context 'with admin' do
subject do
described_class.new(admin, project)
end
it do
is_expected.to be_allowed(:admin_remote_mirror)
end
end
context 'with owner' do
subject do
described_class.new(owner, project)
end
it do
is_expected.to be_allowed(:admin_remote_mirror)
end
end
context 'with developer' do
subject do
described_class.new(developer, project)
end
it do
is_expected.to be_disallowed(:admin_remote_mirror)
end
end
end
context 'with remote mirror setting disabled' do
before do
stub_application_setting(remote_mirror_available: false)
end
context 'with admin' do
subject do
described_class.new(admin, project)
end
it do
is_expected.to be_allowed(:admin_remote_mirror)
end
end
context 'with owner' do
subject do
described_class.new(owner, project)
end
it do
is_expected.to be_disallowed(:admin_remote_mirror)
end
end
end
end
end
...@@ -120,14 +120,6 @@ describe RemoteMirror do ...@@ -120,14 +120,6 @@ describe RemoteMirror do
end end
end end
context 'without project' do
it 'returns nil' do
allow_any_instance_of(described_class).to receive(:project).and_return(nil)
expect(remote_mirror.sync).to be_nil
end
end
context 'as a Geo secondary' do context 'as a Geo secondary' do
it 'returns nil' do it 'returns nil' do
allow(Gitlab::Geo).to receive(:secondary?).and_return(true) allow(Gitlab::Geo).to receive(:secondary?).and_return(true)
......
...@@ -21,17 +21,62 @@ describe GitPushService do ...@@ -21,17 +21,62 @@ describe GitPushService do
described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
end end
it 'fails stuck remote mirrors' do context 'when remote mirror feature is enabled' do
allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors) it 'fails stuck remote mirrors' do
expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!) allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors)
expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!)
subject.execute subject.execute
end
it 'updates remote mirrors' do
expect(project).to receive(:update_remote_mirrors)
subject.execute
end
end end
it 'updates remote mirrors' do context 'when remote mirror feature is disabled' do
expect(project).to receive(:update_remote_mirrors) before do
stub_application_setting(remote_mirror_available: false)
end
context 'with remote mirrors global setting overridden' do
before do
project.remote_mirror_available_overridden = true
end
it 'fails stuck remote mirrors' do
allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors)
expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!)
subject.execute
end
it 'updates remote mirrors' do
expect(project).to receive(:update_remote_mirrors)
subject.execute subject.execute
end
end
context 'without remote mirrors global setting overridden' do
before do
project.remote_mirror_available_overridden = false
end
it 'does not fails stuck remote mirrors' do
expect(project).not_to receive(:mark_stuck_remote_mirrors_as_failed!)
subject.execute
end
it 'does not updates remote mirrors' do
expect(project).not_to receive(:update_remote_mirrors)
subject.execute
end
end
end end
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