Skip to content
Snippets Groups Projects
Commit e1443690 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent d466ee50
No related branches found
No related tags found
No related merge requests found
Showing
with 150 additions and 16 deletions
Loading
@@ -111,6 +111,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
Loading
@@ -111,6 +111,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
end end
end end
   
if Gitlab::Utils.to_boolean(params[:diff_head]) && @merge_request.diffable_merge_ref?
return CompareService.new(@project, @merge_request.merge_ref_head.sha)
.execute(@project, @merge_request.target_branch)
end
if @start_sha if @start_sha
@merge_request_diff.compare_with(@start_sha) @merge_request_diff.compare_with(@start_sha)
else else
Loading
Loading
Loading
@@ -91,6 +91,7 @@ module Issuable
Loading
@@ -91,6 +91,7 @@ module Issuable
validate :description_max_length_for_new_records_is_valid, on: :update validate :description_max_length_for_new_records_is_valid, on: :update
   
before_validation :truncate_description_on_import! before_validation :truncate_description_on_import!
after_save :store_mentions!, if: :any_mentionable_attributes_changed?
   
scope :authored, ->(user) { where(author_id: user) } scope :authored, ->(user) { where(author_id: user) }
scope :recent, -> { reorder(id: :desc) } scope :recent, -> { reorder(id: :desc) }
Loading
Loading
Loading
@@ -99,18 +99,23 @@ module Mentionable
Loading
@@ -99,18 +99,23 @@ module Mentionable
# threw the `ActiveRecord::RecordNotUnique` exception in first place. # threw the `ActiveRecord::RecordNotUnique` exception in first place.
self.class.safe_ensure_unique(retries: 1) do self.class.safe_ensure_unique(retries: 1) do
user_mention = model_user_mention user_mention = model_user_mention
# this may happen due to notes polymorphism, so noteable_id may point to a record that no longer exists
# as we cannot have FK on noteable_id
break if user_mention.blank?
user_mention.mentioned_users_ids = references[:mentioned_users_ids] user_mention.mentioned_users_ids = references[:mentioned_users_ids]
user_mention.mentioned_groups_ids = references[:mentioned_groups_ids] user_mention.mentioned_groups_ids = references[:mentioned_groups_ids]
user_mention.mentioned_projects_ids = references[:mentioned_projects_ids] user_mention.mentioned_projects_ids = references[:mentioned_projects_ids]
   
if user_mention.has_mentions? if user_mention.has_mentions?
user_mention.save! user_mention.save!
elsif user_mention.persisted? else
user_mention.destroy! user_mention.destroy!
end end
true
end end
true
end end
   
def referenced_users def referenced_users
Loading
@@ -218,6 +223,12 @@ module Mentionable
Loading
@@ -218,6 +223,12 @@ module Mentionable
source.select { |key, val| mentionable.include?(key) } source.select { |key, val| mentionable.include?(key) }
end end
   
def any_mentionable_attributes_changed?
self.class.mentionable_attrs.any? do |attr|
saved_changes.key?(attr.first)
end
end
# Determine whether or not a cross-reference Note has already been created between this Mentionable and # Determine whether or not a cross-reference Note has already been created between this Mentionable and
# the specified target. # the specified target.
def cross_reference_exists?(target) def cross_reference_exists?(target)
Loading
Loading
Loading
@@ -39,6 +39,7 @@ class Deployment < ApplicationRecord
Loading
@@ -39,6 +39,7 @@ class Deployment < ApplicationRecord
scope :for_status, -> (status) { where(status: status) } scope :for_status, -> (status) { where(status: status) }
   
scope :visible, -> { where(status: %i[running success failed canceled]) } scope :visible, -> { where(status: %i[running success failed canceled]) }
scope :stoppable, -> { where.not(on_stop: nil).where.not(deployable_id: nil).success }
   
state_machine :status, initial: :created do state_machine :status, initial: :created do
event :run do event :run do
Loading
Loading
Loading
@@ -61,6 +61,7 @@ class Environment < ApplicationRecord
Loading
@@ -61,6 +61,7 @@ class Environment < ApplicationRecord
scope :in_review_folder, -> { where(environment_type: "review") } scope :in_review_folder, -> { where(environment_type: "review") }
scope :for_name, -> (name) { where(name: name) } scope :for_name, -> (name) { where(name: name) }
scope :preload_cluster, -> { preload(last_deployment: :cluster) } scope :preload_cluster, -> { preload(last_deployment: :cluster) }
scope :auto_stoppable, -> (limit) { available.where('auto_stop_at < ?', Time.zone.now).limit(limit) }
   
## ##
# Search environments which have names like the given query. # Search environments which have names like the given query.
Loading
@@ -107,6 +108,44 @@ class Environment < ApplicationRecord
Loading
@@ -107,6 +108,44 @@ class Environment < ApplicationRecord
find_or_create_by(name: name) find_or_create_by(name: name)
end end
   
class << self
##
# This method returns stop actions (jobs) for multiple environments within one
# query. It's useful to avoid N+1 problem.
#
# NOTE: The count of environments should be small~medium (e.g. < 5000)
def stop_actions
cte = cte_for_deployments_with_stop_action
ci_builds = Ci::Build.arel_table
inner_join_stop_actions = ci_builds.join(cte.table).on(
ci_builds[:project_id].eq(cte.table[:project_id])
.and(ci_builds[:ref].eq(cte.table[:ref]))
.and(ci_builds[:name].eq(cte.table[:on_stop]))
).join_sources
pipeline_ids = ci_builds.join(cte.table).on(
ci_builds[:id].eq(cte.table[:deployable_id])
).project(:commit_id)
Ci::Build.joins(inner_join_stop_actions)
.with(cte.to_arel)
.where(ci_builds[:commit_id].in(pipeline_ids))
.where(status: HasStatus::BLOCKED_STATUS)
.preload_project_and_pipeline_project
.preload(:user, :metadata, :deployment)
end
private
def cte_for_deployments_with_stop_action
Gitlab::SQL::CTE.new(:deployments_with_stop_action,
Deployment.where(environment_id: select(:id))
.distinct_on_environment
.stoppable)
end
end
def clear_prometheus_reactive_cache!(query_name) def clear_prometheus_reactive_cache!(query_name)
cluster_prometheus_adapter&.clear_prometheus_reactive_cache!(query_name, self) cluster_prometheus_adapter&.clear_prometheus_reactive_cache!(query_name, self)
end end
Loading
Loading
Loading
@@ -45,7 +45,7 @@ class Issue < ApplicationRecord
Loading
@@ -45,7 +45,7 @@ class Issue < ApplicationRecord
has_many :issue_assignees has_many :issue_assignees
has_many :assignees, class_name: "User", through: :issue_assignees has_many :assignees, class_name: "User", through: :issue_assignees
has_many :zoom_meetings has_many :zoom_meetings
has_many :user_mentions, class_name: "IssueUserMention" has_many :user_mentions, class_name: "IssueUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :sentry_issue has_one :sentry_issue
   
accepts_nested_attributes_for :sentry_issue accepts_nested_attributes_for :sentry_issue
Loading
Loading
Loading
@@ -77,7 +77,7 @@ class MergeRequest < ApplicationRecord
Loading
@@ -77,7 +77,7 @@ class MergeRequest < ApplicationRecord
   
has_many :merge_request_assignees has_many :merge_request_assignees
has_many :assignees, class_name: "User", through: :merge_request_assignees has_many :assignees, class_name: "User", through: :merge_request_assignees
has_many :user_mentions, class_name: "MergeRequestUserMention" has_many :user_mentions, class_name: "MergeRequestUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
   
has_many :deployment_merge_requests has_many :deployment_merge_requests
   
Loading
@@ -840,6 +840,10 @@ class MergeRequest < ApplicationRecord
Loading
@@ -840,6 +840,10 @@ class MergeRequest < ApplicationRecord
end end
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
   
def diffable_merge_ref?
Feature.enabled?(:diff_compare_with_head, target_project) && can_be_merged? && merge_ref_head.present?
end
# Returns boolean indicating the merge_status should be rechecked in order to # Returns boolean indicating the merge_status should be rechecked in order to
# switch to either can_be_merged or cannot_be_merged. # switch to either can_be_merged or cannot_be_merged.
def recheck_merge_status? def recheck_merge_status?
Loading
Loading
Loading
@@ -157,6 +157,7 @@ class Note < ApplicationRecord
Loading
@@ -157,6 +157,7 @@ class Note < ApplicationRecord
after_save :expire_etag_cache, unless: :importing? after_save :expire_etag_cache, unless: :importing?
after_save :touch_noteable, unless: :importing? after_save :touch_noteable, unless: :importing?
after_destroy :expire_etag_cache after_destroy :expire_etag_cache
after_save :store_mentions!, if: :any_mentionable_attributes_changed?
   
class << self class << self
def model_name def model_name
Loading
@@ -498,6 +499,8 @@ class Note < ApplicationRecord
Loading
@@ -498,6 +499,8 @@ class Note < ApplicationRecord
end end
   
def user_mentions def user_mentions
return Note.none unless noteable.present?
noteable.user_mentions.where(note: self) noteable.user_mentions.where(note: self)
end end
   
Loading
@@ -506,6 +509,8 @@ class Note < ApplicationRecord
Loading
@@ -506,6 +509,8 @@ class Note < ApplicationRecord
# Using this method followed by a call to `save` may result in ActiveRecord::RecordNotUnique exception # Using this method followed by a call to `save` may result in ActiveRecord::RecordNotUnique exception
# in a multithreaded environment. Make sure to use it within a `safe_ensure_unique` block. # in a multithreaded environment. Make sure to use it within a `safe_ensure_unique` block.
def model_user_mention def model_user_mention
return if user_mentions.is_a?(ActiveRecord::NullRelation)
user_mentions.first_or_initialize user_mentions.first_or_initialize
end end
   
Loading
Loading
Loading
@@ -41,7 +41,7 @@ class Snippet < ApplicationRecord
Loading
@@ -41,7 +41,7 @@ class Snippet < ApplicationRecord
belongs_to :project belongs_to :project
   
has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :user_mentions, class_name: "SnippetUserMention" has_many :user_mentions, class_name: "SnippetUserMention", dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :snippet_repository, inverse_of: :snippet has_one :snippet_repository, inverse_of: :snippet
   
delegate :name, :email, to: :author, prefix: true, allow_nil: true delegate :name, :email, to: :author, prefix: true, allow_nil: true
Loading
@@ -66,6 +66,8 @@ class Snippet < ApplicationRecord
Loading
@@ -66,6 +66,8 @@ class Snippet < ApplicationRecord
   
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
   
after_save :store_mentions!, if: :any_mentionable_attributes_changed?
# Scopes # Scopes
scope :are_internal, -> { where(visibility_level: Snippet::INTERNAL) } scope :are_internal, -> { where(visibility_level: Snippet::INTERNAL) }
scope :are_private, -> { where(visibility_level: Snippet::PRIVATE) } scope :are_private, -> { where(visibility_level: Snippet::PRIVATE) }
Loading
Loading
Loading
@@ -34,6 +34,14 @@ class MergeRequestDiffEntity < Grape::Entity
Loading
@@ -34,6 +34,14 @@ class MergeRequestDiffEntity < Grape::Entity
merge_request_version_path(project, merge_request, merge_request_diff) merge_request_version_path(project, merge_request, merge_request_diff)
end end
   
expose :head_version_path do |merge_request_diff|
project = merge_request.target_project
next unless project && merge_request.diffable_merge_ref?
diffs_project_merge_request_path(project, merge_request, diff_head: true)
end
expose :version_path do |merge_request_diff| expose :version_path do |merge_request_diff|
start_sha = options[:start_sha] start_sha = options[:start_sha]
project = merge_request.target_project project = merge_request.target_project
Loading
Loading
Loading
@@ -16,6 +16,22 @@ module Ci
Loading
@@ -16,6 +16,22 @@ module Ci
merge_request.environments.each { |environment| stop(environment) } merge_request.environments.each { |environment| stop(environment) }
end end
   
##
# This method is for stopping multiple environments in a batch style.
# The maximum acceptable count of environments is roughly 5000. Please
# apply acceptable `LIMIT` clause to the `environments` relation.
def self.execute_in_batch(environments)
stop_actions = environments.stop_actions.load
environments.update_all(auto_stop_at: nil, state: 'stopped')
stop_actions.each do |stop_action|
stop_action.play(stop_action.user)
rescue => e
Gitlab::ErrorTracking.track_error(e, deployable_id: stop_action.id)
end
end
private private
   
def environments def environments
Loading
Loading
# frozen_string_literal: true
module Environments
class AutoStopService
include ::Gitlab::ExclusiveLeaseHelpers
include ::Gitlab::LoopHelpers
BATCH_SIZE = 100
LOOP_TIMEOUT = 45.minutes
LOOP_LIMIT = 1000
EXCLUSIVE_LOCK_KEY = 'environments:auto_stop:lock'
LOCK_TIMEOUT = 50.minutes
##
# Stop expired environments on GitLab instance
#
# This auto stop process cannot run for more than 45 minutes. This is for
# preventing multiple `AutoStopCronWorker` CRON jobs run concurrently,
# which is scheduled at every hour.
def execute
in_lock(EXCLUSIVE_LOCK_KEY, ttl: LOCK_TIMEOUT, retries: 1) do
loop_until(timeout: LOOP_TIMEOUT, limit: LOOP_LIMIT) do
stop_in_batch
end
end
end
private
def stop_in_batch
environments = Environment.auto_stoppable(BATCH_SIZE)
return false unless environments.exists? && Feature.enabled?(:auto_stop_environments)
Ci::StopEnvironmentsService.execute_in_batch(environments)
end
end
end
Loading
@@ -168,7 +168,7 @@ class IssuableBaseService < BaseService
Loading
@@ -168,7 +168,7 @@ class IssuableBaseService < BaseService
before_create(issuable) before_create(issuable)
   
issuable_saved = issuable.with_transaction_returning_status do issuable_saved = issuable.with_transaction_returning_status do
issuable.save && issuable.store_mentions! issuable.save
end end
   
if issuable_saved if issuable_saved
Loading
@@ -233,7 +233,7 @@ class IssuableBaseService < BaseService
Loading
@@ -233,7 +233,7 @@ class IssuableBaseService < BaseService
ensure_milestone_available(issuable) ensure_milestone_available(issuable)
   
issuable_saved = issuable.with_transaction_returning_status do issuable_saved = issuable.with_transaction_returning_status do
issuable.save(touch: should_touch) && issuable.store_mentions! issuable.save(touch: should_touch)
end end
   
if issuable_saved if issuable_saved
Loading
Loading
Loading
@@ -2,7 +2,6 @@
Loading
@@ -2,7 +2,6 @@
   
module Notes module Notes
class CreateService < ::Notes::BaseService class CreateService < ::Notes::BaseService
# rubocop:disable Metrics/CyclomaticComplexity
def execute def execute
note = Notes::BuildService.new(project, current_user, params.except(:merge_request_diff_head_sha)).execute note = Notes::BuildService.new(project, current_user, params.except(:merge_request_diff_head_sha)).execute
   
Loading
@@ -34,7 +33,7 @@ module Notes
Loading
@@ -34,7 +33,7 @@ module Notes
end end
   
note_saved = note.with_transaction_returning_status do note_saved = note.with_transaction_returning_status do
!only_commands && note.save && note.store_mentions! !only_commands && note.save
end end
   
if note_saved if note_saved
Loading
@@ -67,7 +66,6 @@ module Notes
Loading
@@ -67,7 +66,6 @@ module Notes
   
note note
end end
# rubocop:enable Metrics/CyclomaticComplexity
   
private private
   
Loading
Loading
Loading
@@ -10,7 +10,7 @@ module Notes
Loading
@@ -10,7 +10,7 @@ module Notes
note.assign_attributes(params.merge(updated_by: current_user)) note.assign_attributes(params.merge(updated_by: current_user))
   
note.with_transaction_returning_status do note.with_transaction_returning_status do
note.save && note.store_mentions! note.save
end end
   
only_commands = false only_commands = false
Loading
Loading
Loading
@@ -24,7 +24,7 @@ module Snippets
Loading
@@ -24,7 +24,7 @@ module Snippets
spam_check(snippet, current_user) spam_check(snippet, current_user)
   
snippet_saved = snippet.with_transaction_returning_status do snippet_saved = snippet.with_transaction_returning_status do
snippet.save && snippet.store_mentions! snippet.save
end end
   
if snippet_saved if snippet_saved
Loading
Loading
Loading
@@ -21,7 +21,7 @@ module Snippets
Loading
@@ -21,7 +21,7 @@ module Snippets
spam_check(snippet, current_user) spam_check(snippet, current_user)
   
snippet_saved = snippet.with_transaction_returning_status do snippet_saved = snippet.with_transaction_returning_status do
snippet.save && snippet.store_mentions! snippet.save
end end
   
if snippet_saved if snippet_saved
Loading
Loading
Loading
@@ -70,7 +70,7 @@
Loading
@@ -70,7 +70,7 @@
label_class: 'label-bold' } label_class: 'label-bold' }
.form-text.text-muted .form-text.text-muted
= s_('ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster.') = s_('ClusterIntegration|Uses the Cloud Run, Istio, and HTTP Load Balancing addons for this cluster.')
= link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'cloud-run-on-gke'), target: '_blank' = link_to _('More information'), help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'cloud-run-for-anthos'), target: '_blank'
   
.form-group .form-group
= field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'),
Loading
Loading
Loading
@@ -34,7 +34,7 @@
Loading
@@ -34,7 +34,7 @@
environments_help_path: help_page_path('ci/environments', anchor: 'defining-environments'), environments_help_path: help_page_path('ci/environments', anchor: 'defining-environments'),
clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'), clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'),
deploy_boards_help_path: help_page_path('user/project/deploy_boards.html', anchor: 'enabling-deploy-boards'), deploy_boards_help_path: help_page_path('user/project/deploy_boards.html', anchor: 'enabling-deploy-boards'),
cloud_run_help_path: help_page_path('user/project/clusters/index.md', anchor: 'cloud-run-on-gke'), cloud_run_help_path: help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'cloud-run-for-anthos'),
manage_prometheus_path: manage_prometheus_path, manage_prometheus_path: manage_prometheus_path,
cluster_id: @cluster.id } } cluster_id: @cluster.id } }
   
Loading
Loading
Loading
@@ -75,6 +75,12 @@
Loading
@@ -75,6 +75,12 @@
:latency_sensitive: :latency_sensitive:
:resource_boundary: :unknown :resource_boundary: :unknown
:weight: 1 :weight: 1
- :name: cronjob:environments_auto_stop_cron
:feature_category: :continuous_delivery
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
:weight: 1
- :name: cronjob:expire_build_artifacts - :name: cronjob:expire_build_artifacts
:feature_category: :continuous_integration :feature_category: :continuous_integration
:has_external_dependencies: :has_external_dependencies:
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment