Commit a537e560 authored by Arturo Herrero's avatar Arturo Herrero

Transfer a project/group inheriting integrations

The approach to transfer a project/group under a new namespace (group)
is the following:
1. Remove the current services.
2. Re-create them again (now with the new settings if they exist under
   the new group).
3. For groups, propagate the new settings to subgroups and projects
   belonging to the group.

It also exclude overriding integration with custom values.
parent fdb230f5
......@@ -70,6 +70,7 @@ class Service < ApplicationRecord
scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) }
scope :inherit_from_id, -> (id) { where(inherit_from_id: id) }
scope :inherit, -> { where.not(inherit_from_id: nil) }
scope :for_group, -> (group) { where(group_id: group, type: available_services_types(include_project_specific: false)) }
scope :for_template, -> { where(template: true, type: available_services_types(include_project_specific: false)) }
scope :for_instance, -> { where(instance: true, type: available_services_types(include_project_specific: false)) }
......@@ -278,7 +279,7 @@ class Service < ApplicationRecord
active.where(instance: true),
active.where(group_id: group_ids, inherit_from_id: nil)
]).order(Arel.sql("type ASC, array_position(#{array}::bigint[], services.group_id), instance DESC")).group_by(&:type).each do |type, records|
build_from_integration(records.first, association => scope.id).save!
build_from_integration(records.first, association => scope.id).save
end
end
......
......@@ -28,9 +28,11 @@ module Groups
Group.transaction do
update_group_attributes
ensure_ownership
update_integrations
end
post_update_hooks(@updated_project_ids)
propagate_integrations
true
end
......@@ -196,6 +198,17 @@ module Groups
raise TransferError, result[:message] unless result[:status] == :success
end
end
def update_integrations
@group.services.inherit.delete_all
Service.create_from_active_default_integrations(@group, :group_id)
end
def propagate_integrations
@group.services.inherit.each do |integration|
PropagateIntegrationWorker.perform_async(integration.id)
end
end
end
end
......
......@@ -87,6 +87,8 @@ module Projects
# Move uploads
move_project_uploads(project)
update_integrations
project.old_path_with_namespace = @old_path
update_repository_configuration(@new_path)
......@@ -214,6 +216,11 @@ module Projects
project.shared_runners_enabled = false
end
end
def update_integrations
project.services.inherit.delete_all
Service.create_from_active_default_integrations(project, :project_id)
end
end
end
......
---
title: Transfer a project/group to a new namespace inheriting integrations
merge_request: 48621
author:
type: changed
......@@ -238,8 +238,13 @@ RSpec.describe Groups::TransferService do
end
context 'when the group is allowed to be transferred' do
let_it_be(:new_parent_group_integration) { create(:slack_service, group: new_parent_group, project: nil, webhook: 'http://new-group.slack.com') }
before do
allow(PropagateIntegrationWorker).to receive(:perform_async)
create(:group_member, :owner, group: new_parent_group, user: user)
transfer_service.execute(new_parent_group)
end
......@@ -265,6 +270,30 @@ RSpec.describe Groups::TransferService do
end
end
context 'with a group integration' do
let_it_be(:instance_integration) { create(:slack_service, :instance, webhook: 'http://project.slack.com') }
let(:new_created_integration) { Service.find_by(group: group) }
context 'with an inherited integration' do
let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com', inherit_from_id: instance_integration.id) }
it 'replaces inherited integrations', :aggregate_failures do
expect(new_created_integration.webhook).to eq(new_parent_group_integration.webhook)
expect(PropagateIntegrationWorker).to have_received(:perform_async).with(new_created_integration.id)
expect(Service.count).to eq(3)
end
end
context 'with a custom integration' do
let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com') }
it 'does not updates the integrations', :aggregate_failures do
expect { transfer_service.execute(new_parent_group) }.not_to change { group_integration.webhook }
expect(PropagateIntegrationWorker).not_to have_received(:perform_async)
end
end
end
it 'updates visibility for the group based on the parent group' do
expect(group.visibility_level).to eq(new_parent_group.visibility_level)
end
......
......@@ -7,6 +7,7 @@ RSpec.describe Projects::TransferService do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:group_integration) { create(:slack_service, group: group, project: nil, webhook: 'http://group.slack.com') }
let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) }
subject(:execute_transfer) { described_class.new(project, user).execute(group).tap { project.reload } }
......@@ -117,6 +118,30 @@ RSpec.describe Projects::TransferService do
shard_name: project.repository_storage
)
end
context 'with a project integration' do
let_it_be_with_reload(:project) { create(:project, namespace: user.namespace) }
let_it_be(:instance_integration) { create(:slack_service, :instance, webhook: 'http://project.slack.com') }
context 'with an inherited integration' do
let_it_be(:project_integration) { create(:slack_service, project: project, webhook: 'http://project.slack.com', inherit_from_id: instance_integration.id) }
it 'replaces inherited integrations', :aggregate_failures do
execute_transfer
expect(project.slack_service.webhook).to eq(group_integration.webhook)
expect(Service.count).to eq(3)
end
end
context 'with a custom integration' do
let_it_be(:project_integration) { create(:slack_service, project: project, webhook: 'http://project.slack.com') }
it 'does not updates the integrations' do
expect { execute_transfer }.not_to change { project.slack_service.webhook }
end
end
end
end
context 'when transfer fails' do
......
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