Commit 46e1e292 authored by Stan Hu's avatar Stan Hu

Merge branch 'vshumilo-keep-namespace-name-in-sync' into 'master'

Keep namespace name in sync with CustomersDot

See merge request gitlab-org/gitlab!69896
parents f4ceb5df 5ab38dd0
......@@ -249,6 +249,8 @@
- 1
- - namespaces_onboarding_user_added
- 1
- - namespaces_sync_namespace_name
- 1
- - new_epic
- 2
- - new_issue
......
......@@ -106,6 +106,8 @@ module EE
# Changing the plan or other details may invalidate this cache
before_save :clear_feature_available_cache
after_commit :sync_name_with_customers_dot, on: :update, if: -> { name_previously_changed? }
end
# Only groups can be marked for deletion
......@@ -438,6 +440,13 @@ module EE
clear_memoization(:feature_available)
end
def sync_name_with_customers_dot
return unless ::Feature.enabled?(:sync_namespace_name_with_cdot)
return unless ::Gitlab.com?
::Namespaces::SyncNamespaceNameWorker.perform_async(id)
end
def load_feature_available(feature)
globally_available = License.feature_available?(feature)
......
......@@ -1074,6 +1074,15 @@
:weight: 1
:idempotent: true
:tags: []
- :name: namespaces_sync_namespace_name
:worker_name: Namespaces::SyncNamespaceNameWorker
:feature_category: :license
:has_external_dependencies: true
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: new_epic
:worker_name: NewEpicWorker
:feature_category: :epics
......
# frozen_string_literal: true
module Namespaces
class SyncNamespaceNameWorker
include ApplicationWorker
data_consistency :always
feature_category :license
deduplicate :until_executing
idempotent!
worker_has_external_dependencies!
RequestError = Class.new(StandardError)
def perform(namespace_id)
namespace = Namespace.find_by_id(namespace_id)
return unless namespace
response = client.update_namespace_name(namespace.id, namespace.name)
return if response[:success]
raise RequestError, "Namespace name sync failed! Namespace id: #{namespace.id}"
end
private
def client
Gitlab::SubscriptionPortal::Client
end
end
end
---
name: sync_namespace_name_with_cdot
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69896
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341056
milestone: '14.3'
type: development
group: group::license
default_enabled: false
......@@ -158,6 +158,43 @@ module Gitlab
end
end
def update_namespace_name(namespace_id, namespace_name)
variables = {
namespaceId: namespace_id,
namespaceName: namespace_name
}
query = <<~GQL
mutation($namespaceId: ID!, $namespaceName: String!) {
orderNamespaceNameUpdate(
input: {
namespaceId: $namespaceId,
namespaceName: $namespaceName
}
) {
errors
}
}
GQL
response = execute_graphql_query(
{ query: query, variables: variables }
)
return error(CONNECTIVITY_ERROR) unless response[:success]
response = response.dig(:data, 'data', 'orderNamespaceNameUpdate')
if response['errors'].blank?
{ success: true }
else
error(response['errors'])
end
rescue Gitlab::HTTP::BlockedUrlError, HTTParty::Error, Errno::ECONNREFUSED, Errno::ECONNRESET, SocketError, Timeout::Error => e
Gitlab::ErrorTracking.log_exception(e)
error(CONNECTIVITY_ERROR)
end
private
def execute_graphql_query(params)
......
......@@ -380,4 +380,66 @@ RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
end
end
end
describe '#update_namespace_name' do
subject(:update_request) do
client.update_namespace_name('namespace id', 'namespace name')
end
it 'returns success' do
expect(client).to receive(:execute_graphql_query).and_return(
{
success: true,
data: {
'data' => {
'orderNamespaceNameUpdate' => {
'errors' => []
}
}
}
}
)
result = update_request
expect(result).to eq({ success: true })
end
it 'returns failure' do
expect(client).to receive(:execute_graphql_query).and_return(
{
success: true,
data: {
'data' => {
'orderNamespaceNameUpdate' => {
'errors' => ['error updating the name']
}
}
}
}
)
result = update_request
expect(result).to eq({ errors: ['error updating the name'], success: false })
end
it 'returns connectivity error when remote server returns error' do
stub_request(:any, EE::SUBSCRIPTIONS_GRAPHQL_URL).to_return(status: [500, "Internal Server Error"])
result = update_request
expect(result).to eq({ errors: described_class::CONNECTIVITY_ERROR, success: false })
end
it 'returns connectivity error when the remote server is unreachable' do
stub_request(:any, EE::SUBSCRIPTIONS_GRAPHQL_URL).to_timeout
allow(Gitlab::ErrorTracking).to receive(:log_exception)
result = update_request
expect(result).to eq({ errors: described_class::CONNECTIVITY_ERROR, success: false })
expect(Gitlab::ErrorTracking).to have_received(:log_exception).with(kind_of(Timeout::Error))
end
end
end
......@@ -359,6 +359,58 @@ RSpec.describe Namespace do
end
end
describe 'after_commit :sync_name_with_customers_dot' do
let(:namespace) { create(:group) }
subject(:update_namespace) { namespace.update!(attributes) }
before do
stub_feature_flags(sync_namespace_name_with_cdot: true)
allow(Gitlab).to receive(:com?).and_return(true)
end
shared_examples 'no sync' do
it 'does not trigger a sync with CustomersDot' do
expect(::Namespaces::SyncNamespaceNameWorker).not_to receive(:perform_async)
update_namespace
end
end
context 'when the name is not updated' do
let(:attributes) { { path: 'Foo' } }
include_examples 'no sync'
end
context 'when the name is updated' do
let(:attributes) { { name: 'Foo' } }
context 'with :sync_namespace_name_with_cdot feature flag disabled' do
before do
stub_feature_flags(sync_namespace_name_with_cdot: false)
end
include_examples 'no sync'
end
context 'when not on Gitlab.com?' do
before do
allow(Gitlab).to receive(:com?).and_return(false)
end
include_examples 'no sync'
end
it 'triggers a name sync with CustomersDot' do
expect(::Namespaces::SyncNamespaceNameWorker).to receive(:perform_async)
.with(namespace.id).once
update_namespace
end
end
end
describe '#move_dir' do
context 'when running on a primary node' do
let_it_be(:primary) { create(:geo_node, :primary) }
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Namespaces::SyncNamespaceNameWorker, type: :worker do
let_it_be(:namespace) { create(:group) }
describe '#perform' do
let(:namespace_id) { namespace.id }
subject(:sync) do
described_class.new.perform(namespace_id)
end
context 'when the namespace is not found' do
let(:namespace_id) { 'ABC' }
it 'does not trigger a sync for the namespace name to CustomersDot' do
expect(Gitlab::SubscriptionPortal::Client).not_to receive(:update_namespace_name)
sync
end
it 'does not raise an error' do
expect { sync }.not_to raise_error
end
end
context 'when the sync fails' do
it 'raises a RequestError' do
expect(Gitlab::SubscriptionPortal::Client).to receive(:update_namespace_name)
.and_return({ success: false })
expect { sync }.to raise_error(
described_class::RequestError,
"Namespace name sync failed! Namespace id: #{namespace_id}"
)
end
end
it 'triggers a sync for the namespace name to CustomersDot' do
expect(Gitlab::SubscriptionPortal::Client).to receive(:update_namespace_name)
.with(namespace.id, namespace.name)
.and_return({ success: true })
sync
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