Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-11 21:08:58 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-11 21:08:58 +0300
commit1ca9950d5f890cd8f185e1eda158b969a7244fe2 (patch)
tree6f62715938a4b2b001705c51c697609a8e0850ae /app
parentbcc77054ee9aefd1e332e04a4189390fd5a3112e (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin/services_controller.rb12
-rw-r--r--app/controllers/projects/serverless/functions_controller.rb15
-rw-r--r--app/finders/projects/serverless/functions_finder.rb16
-rw-r--r--app/graphql/types/snippets/blob_type.rb4
-rw-r--r--app/models/clusters/cluster.rb6
-rw-r--r--app/models/pages_domain.rb4
-rw-r--r--app/models/project.rb14
-rw-r--r--app/models/project_services/issue_tracker_service.rb2
-rw-r--r--app/models/project_services/prometheus_service.rb2
-rw-r--r--app/models/service.rb20
-rw-r--r--app/presenters/snippet_blob_presenter.rb12
-rw-r--r--app/serializers/projects/serverless/service_entity.rb88
-rw-r--r--app/services/projects/create_service.rb8
-rw-r--r--app/services/projects/propagate_service_template.rb (renamed from app/services/projects/propagate_instance_level_service.rb)28
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_help.html.haml4
-rw-r--r--app/views/projects/services/slack_slash_commands/_help.html.haml2
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/pages_domain_removal_cron_worker.rb6
-rw-r--r--app/workers/pages_domain_ssl_renewal_cron_worker.rb8
-rw-r--r--app/workers/pages_domain_verification_cron_worker.rb8
-rw-r--r--app/workers/propagate_instance_level_service_worker.rb26
-rw-r--r--app/workers/propagate_service_template_worker.rb26
22 files changed, 146 insertions, 167 deletions
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index 9cea25cc7fe..e31e0e09978 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -7,7 +7,7 @@ class Admin::ServicesController < Admin::ApplicationController
before_action :service, only: [:edit, :update]
def index
- @services = instance_level_services
+ @services = services_templates
end
def edit
@@ -19,7 +19,7 @@ class Admin::ServicesController < Admin::ApplicationController
def update
if service.update(service_params[:service])
- PropagateInstanceLevelServiceWorker.perform_async(service.id) if service.active?
+ PropagateServiceTemplateWorker.perform_async(service.id) if service.active?
redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully'
@@ -31,17 +31,17 @@ class Admin::ServicesController < Admin::ApplicationController
private
# rubocop: disable CodeReuse/ActiveRecord
- def instance_level_services
+ def services_templates
Service.available_services_names.map do |service_name|
- service = "#{service_name}_service".camelize.constantize
- service.where(instance: true).first_or_create
+ service_template = "#{service_name}_service".camelize.constantize
+ service_template.where(template: true).first_or_create
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def service
- @service ||= Service.where(id: params[:id], instance: true).first
+ @service ||= Service.where(id: params[:id], template: true).first
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/projects/serverless/functions_controller.rb b/app/controllers/projects/serverless/functions_controller.rb
index 4b0d001fca6..0b55414d390 100644
--- a/app/controllers/projects/serverless/functions_controller.rb
+++ b/app/controllers/projects/serverless/functions_controller.rb
@@ -8,11 +8,15 @@ module Projects
def index
respond_to do |format|
format.json do
- functions = finder.execute
+ functions = finder.execute.select do |function|
+ can?(@current_user, :read_cluster, function.cluster)
+ end
+
+ serialized_functions = serialize_function(functions)
render json: {
knative_installed: finder.knative_installed,
- functions: serialize_function(functions)
+ functions: serialized_functions
}.to_json
end
@@ -23,11 +27,14 @@ module Projects
end
def show
- @service = serialize_function(finder.service(params[:environment_id], params[:id]))
- @prometheus = finder.has_prometheus?(params[:environment_id])
+ function = finder.service(params[:environment_id], params[:id])
+ return not_found unless function && can?(@current_user, :read_cluster, function.cluster)
+ @service = serialize_function(function)
return not_found if @service.nil?
+ @prometheus = finder.has_prometheus?(params[:environment_id])
+
respond_to do |format|
format.json do
render json: @service
diff --git a/app/finders/projects/serverless/functions_finder.rb b/app/finders/projects/serverless/functions_finder.rb
index 4e0b69f47e5..3b4ecbb5387 100644
--- a/app/finders/projects/serverless/functions_finder.rb
+++ b/app/finders/projects/serverless/functions_finder.rb
@@ -93,24 +93,32 @@ module Projects
.services
.select { |svc| svc["metadata"]["name"] == name }
- add_metadata(finder, services).first unless services.nil?
+ attributes = add_metadata(finder, services).first
+ next unless attributes
+
+ Gitlab::Serverless::Service.new(attributes)
end
end
def knative_services
services_finders.map do |finder|
- services = finder.services
+ attributes = add_metadata(finder, finder.services)
- add_metadata(finder, services) unless services.nil?
+ attributes&.map do |attributes|
+ Gitlab::Serverless::Service.new(attributes)
+ end
end
end
def add_metadata(finder, services)
+ return if services.nil?
+
add_pod_count = services.one?
services.each do |s|
s["environment_scope"] = finder.cluster.environment_scope
- s["cluster_id"] = finder.cluster.id
+ s["environment"] = finder.environment
+ s["cluster"] = finder.cluster
if add_pod_count
s["podcount"] = finder
diff --git a/app/graphql/types/snippets/blob_type.rb b/app/graphql/types/snippets/blob_type.rb
index cacb2177192..d63e08a5b08 100644
--- a/app/graphql/types/snippets/blob_type.rb
+++ b/app/graphql/types/snippets/blob_type.rb
@@ -12,6 +12,10 @@ module Types
description: 'Blob highlighted data',
null: true
+ field :plain_highlighted_data, GraphQL::STRING_TYPE,
+ description: 'Blob plain highlighted data',
+ null: true
+
field :raw_path, GraphQL::STRING_TYPE,
description: 'Blob raw content endpoint path',
null: false
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 7855db960c9..7e76d324bdc 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -290,6 +290,12 @@ module Clusters
end
end
+ def serverless_domain
+ strong_memoize(:serverless_domain) do
+ self.application_knative&.serverless_domain_cluster
+ end
+ end
+
private
def unique_management_project_environment_scope
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 330764ebc7f..91767c53f81 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -62,6 +62,8 @@ class PagesDomain < ApplicationRecord
scope :for_removal, -> { where("remove_at < ?", Time.now) }
+ scope :with_logging_info, -> { includes(project: [:namespace, :route]) }
+
def verified?
!!verified_at
end
@@ -285,3 +287,5 @@ class PagesDomain < ApplicationRecord
!auto_ssl_enabled? && project&.pages_https_only?
end
end
+
+PagesDomain.prepend_if_ee('::EE::PagesDomain')
diff --git a/app/models/project.rb b/app/models/project.rb
index b5639039bb6..44701ef792a 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1224,13 +1224,13 @@ class Project < ApplicationRecord
service = find_service(services, name)
return service if service
- # We should check if an instance-level service exists
- instance_level_service = find_service(instance_level_services, name)
+ # We should check if template for the service exists
+ template = find_service(services_templates, name)
- if instance_level_service
- Service.build_from_instance(id, instance_level_service)
+ if template
+ Service.build_from_template(id, template)
else
- # If no instance-level service exists, we should create a new service. Ex `build_gitlab_ci_service`
+ # If no template, we should create an instance. Ex `build_gitlab_ci_service`
public_send("build_#{name}_service") # rubocop:disable GitlabSecurity/PublicSend
end
end
@@ -2460,8 +2460,8 @@ class Project < ApplicationRecord
end
end
- def instance_level_services
- @instance_level_services ||= Service.where(instance: true)
+ def services_templates
+ @services_templates ||= Service.where(template: true)
end
def ensure_pages_metadatum
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index 2ccf8e094e6..9e1393196ff 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -164,7 +164,7 @@ class IssueTrackerService < Service
end
def one_issue_tracker
- return if instance?
+ return if template?
return if project.blank?
if project.services.external_issue_trackers.where.not(id: id).any?
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 72dabe2578a..00b06ae2595 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -85,7 +85,7 @@ class PrometheusService < MonitoringService
end
def prometheus_available?
- return false if instance?
+ return false if template?
return false unless project
project.all_clusters.enabled.any? { |cluster| cluster.application_prometheus_available? }
diff --git a/app/models/service.rb b/app/models/service.rb
index 6d0e375e757..95b7c6927cf 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -32,7 +32,7 @@ class Service < ApplicationRecord
belongs_to :project, inverse_of: :services
has_one :service_hook
- validates :project_id, presence: true, unless: proc { |service| service.instance? }
+ validates :project_id, presence: true, unless: proc { |service| service.template? }
validates :type, presence: true
scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
@@ -70,8 +70,8 @@ class Service < ApplicationRecord
true
end
- def instance?
- instance
+ def template?
+ template
end
def category
@@ -299,15 +299,15 @@ class Service < ApplicationRecord
service_names.sort_by(&:downcase)
end
- def self.build_from_instance(project_id, instance_level_service)
- service = instance_level_service.dup
+ def self.build_from_template(project_id, template)
+ service = template.dup
- if instance_level_service.supports_data_fields?
- data_fields = instance_level_service.data_fields.dup
+ if template.supports_data_fields?
+ data_fields = template.data_fields.dup
data_fields.service = service
end
- service.instance = false
+ service.template = false
service.project_id = project_id
service.active = false if service.active? && !service.valid?
service
@@ -321,6 +321,10 @@ class Service < ApplicationRecord
nil
end
+ def self.find_by_template
+ find_by(template: true)
+ end
+
# override if needed
def supports_data_fields?
false
diff --git a/app/presenters/snippet_blob_presenter.rb b/app/presenters/snippet_blob_presenter.rb
index 9baaacdbb24..71361b18f5a 100644
--- a/app/presenters/snippet_blob_presenter.rb
+++ b/app/presenters/snippet_blob_presenter.rb
@@ -4,11 +4,13 @@ class SnippetBlobPresenter < BlobPresenter
def highlighted_data
return if blob.binary?
- if blob.rich_viewer&.partial_name == 'markup'
- blob.rendered_markup
- else
- highlight
- end
+ highlight(plain: false)
+ end
+
+ def plain_highlighted_data
+ return if blob.binary?
+
+ highlight(plain: true)
end
def raw_path
diff --git a/app/serializers/projects/serverless/service_entity.rb b/app/serializers/projects/serverless/service_entity.rb
index 10360e575bb..05beb562e40 100644
--- a/app/serializers/projects/serverless/service_entity.rb
+++ b/app/serializers/projects/serverless/service_entity.rb
@@ -5,91 +5,31 @@ module Projects
class ServiceEntity < Grape::Entity
include RequestAwareEntity
- expose :name do |service|
- service.dig('metadata', 'name')
- end
-
- expose :namespace do |service|
- service.dig('metadata', 'namespace')
- end
-
- expose :environment_scope do |service|
- service.dig('environment_scope')
- end
-
- expose :cluster_id do |service|
- service.dig('cluster_id')
- end
+ expose :name
+ expose :namespace
+ expose :environment_scope
+ expose :podcount
+ expose :created_at
+ expose :image
+ expose :description
+ expose :url
expose :detail_url do |service|
project_serverless_path(
request.project,
- service.dig('environment_scope'),
- service.dig('metadata', 'name'))
- end
-
- expose :podcount do |service|
- service.dig('podcount')
+ service.environment_scope,
+ service.name)
end
expose :metrics_url do |service|
project_serverless_metrics_path(
request.project,
- service.dig('environment_scope'),
- service.dig('metadata', 'name')) + ".json"
- end
-
- expose :created_at do |service|
- service.dig('metadata', 'creationTimestamp')
- end
-
- expose :url do |service|
- knative_06_07_url(service) || knative_05_url(service)
- end
-
- expose :description do |service|
- knative_07_description(service) || knative_05_06_description(service)
+ service.environment_scope,
+ service.name, format: :json)
end
- expose :image do |service|
- service.dig(
- 'spec',
- 'runLatest',
- 'configuration',
- 'build',
- 'template',
- 'name')
- end
-
- private
-
- def knative_07_description(service)
- service.dig(
- 'spec',
- 'template',
- 'metadata',
- 'annotations',
- 'Description'
- )
- end
-
- def knative_05_url(service)
- "http://#{service.dig('status', 'domain')}"
- end
-
- def knative_06_07_url(service)
- service.dig('status', 'url')
- end
-
- def knative_05_06_description(service)
- service.dig(
- 'spec',
- 'runLatest',
- 'configuration',
- 'revisionTemplate',
- 'metadata',
- 'annotations',
- 'Description')
+ expose :cluster_id do |service|
+ service.cluster&.id
end
end
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 6b1fb92681a..7bf68e7d315 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -135,7 +135,7 @@ module Projects
if @project.save
unless @project.gitlab_project_import?
- create_services_from_active_instance_level_services(@project)
+ create_services_from_active_templates(@project)
@project.create_labels
end
@@ -161,9 +161,9 @@ module Projects
end
# rubocop: disable CodeReuse/ActiveRecord
- def create_services_from_active_instance_level_services(project)
- Service.where(instance: true, active: true).each do |template|
- service = Service.build_from_instance(project.id, template)
+ def create_services_from_active_templates(project)
+ Service.where(template: true, active: true).each do |template|
+ service = Service.build_from_template(project.id, template)
service.save!
end
end
diff --git a/app/services/projects/propagate_instance_level_service.rb b/app/services/projects/propagate_service_template.rb
index dc75977ba0f..6013b00b8c6 100644
--- a/app/services/projects/propagate_instance_level_service.rb
+++ b/app/services/projects/propagate_service_template.rb
@@ -1,38 +1,38 @@
# frozen_string_literal: true
module Projects
- class PropagateInstanceLevelService
+ class PropagateServiceTemplate
BATCH_SIZE = 100
def self.propagate(*args)
new(*args).propagate
end
- def initialize(instance_level_service)
- @instance_level_service = instance_level_service
+ def initialize(template)
+ @template = template
end
def propagate
- return unless @instance_level_service.active?
+ return unless @template.active?
- Rails.logger.info("Propagating services for instance_level_service #{@instance_level_service.id}") # rubocop:disable Gitlab/RailsLogger
+ Rails.logger.info("Propagating services for template #{@template.id}") # rubocop:disable Gitlab/RailsLogger
- propagate_projects_with_instance_level_service
+ propagate_projects_with_template
end
private
- def propagate_projects_with_instance_level_service
+ def propagate_projects_with_template
loop do
batch = Project.uncached { project_ids_batch }
- bulk_create_from_instance_level_service(batch) unless batch.empty?
+ bulk_create_from_template(batch) unless batch.empty?
break if batch.size < BATCH_SIZE
end
end
- def bulk_create_from_instance_level_service(batch)
+ def bulk_create_from_template(batch)
service_list = batch.map do |project_id|
service_hash.values << project_id
end
@@ -52,7 +52,7 @@ module Projects
SELECT true
FROM services
WHERE services.project_id = projects.id
- AND services.type = '#{@instance_level_service.type}'
+ AND services.type = '#{@template.type}'
)
AND projects.pending_delete = false
AND projects.archived = false
@@ -73,9 +73,9 @@ module Projects
def service_hash
@service_hash ||=
begin
- instance_hash = @instance_level_service.as_json(methods: :type).except('id', 'instance', 'project_id')
+ template_hash = @template.as_json(methods: :type).except('id', 'template', 'project_id')
- instance_hash.each_with_object({}) do |(key, value), service_hash|
+ template_hash.each_with_object({}) do |(key, value), service_hash|
value = value.is_a?(Hash) ? value.to_json : value
service_hash[ActiveRecord::Base.connection.quote_column_name(key)] =
@@ -97,11 +97,11 @@ module Projects
# rubocop: enable CodeReuse/ActiveRecord
def active_external_issue_tracker?
- @instance_level_service.issue_tracker? && !@instance_level_service.default
+ @template.issue_tracker? && !@template.default
end
def active_external_wiki?
- @instance_level_service.type == 'ExternalWikiService'
+ @template.type == 'ExternalWikiService'
end
end
end
diff --git a/app/views/projects/services/mattermost_slash_commands/_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_help.html.haml
index 7f14128a0cb..cc005dd69b7 100644
--- a/app/views/projects/services/mattermost_slash_commands/_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_help.html.haml
@@ -10,8 +10,8 @@
%p.inline
= s_("MattermostService|See list of available commands in Mattermost after setting up this service, by entering")
%kbd.inline /&lt;trigger&gt; help
- - unless enabled || @service.instance?
+ - unless enabled || @service.template?
= render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
-- if enabled && !@service.instance?
+- if enabled && !@service.template?
= render 'projects/services/mattermost_slash_commands/installation_info', subject: @service
diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml
index 447f7f074a8..7f6717e298c 100644
--- a/app/views/projects/services/slack_slash_commands/_help.html.haml
+++ b/app/views/projects/services/slack_slash_commands/_help.html.haml
@@ -11,7 +11,7 @@
%p.inline
= s_("SlackService|See list of available commands in Slack after setting up this service, by entering")
%kbd.inline /&lt;command&gt; help
- - unless @service.instance?
+ - unless @service.template?
%p= _("To set up this service:")
%ul.list-unstyled.indent-list
%li
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 22dd7f5843f..42ee0994617 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -969,7 +969,7 @@
:latency_sensitive:
:resource_boundary: :unknown
:weight: 1
-- :name: propagate_instance_level_service
+- :name: propagate_service_template
:feature_category: :source_code_management
:has_external_dependencies:
:latency_sensitive:
diff --git a/app/workers/pages_domain_removal_cron_worker.rb b/app/workers/pages_domain_removal_cron_worker.rb
index b14d0d4597c..1c96dd6ad8c 100644
--- a/app/workers/pages_domain_removal_cron_worker.rb
+++ b/app/workers/pages_domain_removal_cron_worker.rb
@@ -2,14 +2,14 @@
class PagesDomainRemovalCronWorker
include ApplicationWorker
- include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+ include CronjobQueue
feature_category :pages
worker_resource_boundary :cpu
def perform
- PagesDomain.for_removal.find_each do |domain|
- domain.destroy!
+ PagesDomain.for_removal.with_logging_info.find_each do |domain|
+ with_context(project: domain.project) { domain.destroy! }
rescue => e
Gitlab::ErrorTracking.track_exception(e)
end
diff --git a/app/workers/pages_domain_ssl_renewal_cron_worker.rb b/app/workers/pages_domain_ssl_renewal_cron_worker.rb
index b20ed23dc89..c1201b935d1 100644
--- a/app/workers/pages_domain_ssl_renewal_cron_worker.rb
+++ b/app/workers/pages_domain_ssl_renewal_cron_worker.rb
@@ -2,15 +2,17 @@
class PagesDomainSslRenewalCronWorker
include ApplicationWorker
- include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+ include CronjobQueue
feature_category :pages
def perform
return unless ::Gitlab::LetsEncrypt.enabled?
- PagesDomain.need_auto_ssl_renewal.find_each do |domain|
- PagesDomainSslRenewalWorker.perform_async(domain.id)
+ PagesDomain.need_auto_ssl_renewal.with_logging_info.find_each do |domain|
+ with_context(project: domain.project) do
+ PagesDomainSslRenewalWorker.perform_async(domain.id)
+ end
end
end
end
diff --git a/app/workers/pages_domain_verification_cron_worker.rb b/app/workers/pages_domain_verification_cron_worker.rb
index 8bd7fe33334..b06aa65a8e5 100644
--- a/app/workers/pages_domain_verification_cron_worker.rb
+++ b/app/workers/pages_domain_verification_cron_worker.rb
@@ -2,15 +2,17 @@
class PagesDomainVerificationCronWorker
include ApplicationWorker
- include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+ include CronjobQueue
feature_category :pages
def perform
return if Gitlab::Database.read_only?
- PagesDomain.needs_verification.find_each do |domain|
- PagesDomainVerificationWorker.perform_async(domain.id)
+ PagesDomain.needs_verification.with_logging_info.find_each do |domain|
+ with_context(project: domain.project) do
+ PagesDomainVerificationWorker.perform_async(domain.id)
+ end
end
end
end
diff --git a/app/workers/propagate_instance_level_service_worker.rb b/app/workers/propagate_instance_level_service_worker.rb
deleted file mode 100644
index 64ea61cabfa..00000000000
--- a/app/workers/propagate_instance_level_service_worker.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-# Worker for updating any project specific caches.
-class PropagateInstanceLevelServiceWorker
- include ApplicationWorker
-
- feature_category :source_code_management
-
- LEASE_TIMEOUT = 4.hours.to_i
-
- # rubocop: disable CodeReuse/ActiveRecord
- def perform(instance_level_service_id)
- return unless try_obtain_lease_for(instance_level_service_id)
-
- Projects::PropagateInstanceLevelService.propagate(Service.find_by(id: instance_level_service_id))
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- private
-
- def try_obtain_lease_for(instance_level_service_id)
- Gitlab::ExclusiveLease
- .new("propagate_instance_level_service_worker:#{instance_level_service_id}", timeout: LEASE_TIMEOUT)
- .try_obtain
- end
-end
diff --git a/app/workers/propagate_service_template_worker.rb b/app/workers/propagate_service_template_worker.rb
new file mode 100644
index 00000000000..73a2b453207
--- /dev/null
+++ b/app/workers/propagate_service_template_worker.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# Worker for updating any project specific caches.
+class PropagateServiceTemplateWorker
+ include ApplicationWorker
+
+ feature_category :source_code_management
+
+ LEASE_TIMEOUT = 4.hours.to_i
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(template_id)
+ return unless try_obtain_lease_for(template_id)
+
+ Projects::PropagateServiceTemplate.propagate(Service.find_by(id: template_id))
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ def try_obtain_lease_for(template_id)
+ Gitlab::ExclusiveLease
+ .new("propagate_service_template_worker:#{template_id}", timeout: LEASE_TIMEOUT)
+ .try_obtain
+ end
+end