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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
commit43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch)
treedceebdc68925362117480a5d672bcff122fb625b /app/models/ci
parent20c84b99005abd1c82101dfeff264ac50d2df211 (diff)
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'app/models/ci')
-rw-r--r--app/models/ci/bridge.rb6
-rw-r--r--app/models/ci/build.rb81
-rw-r--r--app/models/ci/build_metadata.rb3
-rw-r--r--app/models/ci/build_need.rb2
-rw-r--r--app/models/ci/build_pending_state.rb2
-rw-r--r--app/models/ci/build_trace_chunk.rb2
-rw-r--r--app/models/ci/build_trace_metadata.rb4
-rw-r--r--app/models/ci/catalog/listing.rb34
-rw-r--r--app/models/ci/catalog/resource.rb28
-rw-r--r--app/models/ci/commit_with_pipeline.rb2
-rw-r--r--app/models/ci/daily_build_group_report_result.rb5
-rw-r--r--app/models/ci/group_variable.rb8
-rw-r--r--app/models/ci/job_artifact.rb6
-rw-r--r--app/models/ci/job_token/scope.rb3
-rw-r--r--app/models/ci/job_variable.rb2
-rw-r--r--app/models/ci/namespace_mirror.rb3
-rw-r--r--app/models/ci/pending_build.rb1
-rw-r--r--app/models/ci/pipeline.rb119
-rw-r--r--app/models/ci/pipeline_schedule.rb13
-rw-r--r--app/models/ci/pipeline_variable.rb3
-rw-r--r--app/models/ci/processable.rb2
-rw-r--r--app/models/ci/project_mirror.rb3
-rw-r--r--app/models/ci/ref.rb3
-rw-r--r--app/models/ci/resource_group.rb23
-rw-r--r--app/models/ci/runner.rb86
-rw-r--r--app/models/ci/runner_manager.rb (renamed from app/models/ci/runner_machine.rb)60
-rw-r--r--app/models/ci/runner_manager_build.rb29
-rw-r--r--app/models/ci/runner_version.rb5
-rw-r--r--app/models/ci/running_build.rb10
-rw-r--r--app/models/ci/sources/pipeline.rb1
-rw-r--r--app/models/ci/stage.rb6
-rw-r--r--app/models/ci/trigger.rb5
32 files changed, 400 insertions, 160 deletions
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 697f06fbffd..7cdd0d56a98 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -55,8 +55,6 @@ module Ci
end
def retryable?
- return false unless Feature.enabled?(:ci_recreate_downstream_pipeline, project)
-
return false if failed? && (pipeline_loop_detected? || reached_max_descendant_pipelines_depth?)
super
@@ -81,7 +79,9 @@ module Ci
case pipeline.status
when 'success'
success!
- when 'failed', 'canceled', 'skipped'
+ when 'canceled'
+ cancel!
+ when 'failed', 'skipped'
drop!
else
false
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 1e70dd171ed..61585de4ff7 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -18,14 +18,15 @@ module Ci
belongs_to :runner
belongs_to :trigger_request
belongs_to :erased_by, class_name: 'User'
- belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
+ belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, inverse_of: :builds
RUNNER_FEATURES = {
upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? },
refspecs: -> (build) { build.merge_request_ref? },
artifacts_exclude: -> (build) { build.supports_artifacts_exclude? },
multi_build_steps: -> (build) { build.multi_build_steps? },
- return_exit_code: -> (build) { build.exit_codes_defined? }
+ return_exit_code: -> (build) { build.exit_codes_defined? },
+ fallback_cache_keys: -> (build) { build.fallback_cache_keys_defined? }
}.freeze
DEGRADATION_THRESHOLD_VARIABLE_NAME = 'DEGRADATION_THRESHOLD'
@@ -35,8 +36,8 @@ module Ci
has_one :deployment, as: :deployable, class_name: 'Deployment', inverse_of: :deployable
has_one :pending_state, class_name: 'Ci::BuildPendingState', foreign_key: :build_id, inverse_of: :build
- has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id
- has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
+ has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id, inverse_of: :build
+ has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id, inverse_of: :build
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id, inverse_of: :build
has_many :report_results, class_name: 'Ci::BuildReportResult', foreign_key: :build_id, inverse_of: :build
has_one :namespace, through: :project
@@ -47,7 +48,7 @@ module Ci
# Details: https://gitlab.com/gitlab-org/gitlab/-/issues/24644#note_689472685
has_many :job_artifacts, class_name: 'Ci::JobArtifact', foreign_key: :job_id, dependent: :destroy, inverse_of: :job # rubocop:disable Cop/ActiveRecordDependent
has_many :job_variables, class_name: 'Ci::JobVariable', foreign_key: :job_id, inverse_of: :job
- has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_job_id
+ has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_job_id, inverse_of: :build
has_many :pages_deployments, foreign_key: :ci_build_id, inverse_of: :ci_build
@@ -55,7 +56,9 @@ module Ci
has_one :"job_artifacts_#{key}", -> { where(file_type: value) }, class_name: 'Ci::JobArtifact', foreign_key: :job_id, inverse_of: :job
end
- has_one :runner_machine, through: :metadata, class_name: 'Ci::RunnerMachine'
+ has_one :runner_manager_build, class_name: 'Ci::RunnerManagerBuild', foreign_key: :build_id, inverse_of: :build,
+ autosave: true
+ has_one :runner_manager, foreign_key: :runner_machine_id, through: :runner_manager_build, class_name: 'Ci::RunnerManager'
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, foreign_key: :build_id, inverse_of: :build
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', foreign_key: :build_id, inverse_of: :build
@@ -71,6 +74,7 @@ module Ci
delegate :gitlab_deploy_token, to: :project
delegate :harbor_integration, to: :project
delegate :apple_app_store_integration, to: :project
+ delegate :google_play_integration, to: :project
delegate :trigger_short_token, to: :trigger_request, allow_nil: true
delegate :ensure_persistent_ref, to: :pipeline
delegate :enable_debug_trace!, to: :metadata
@@ -132,7 +136,7 @@ module Ci
scope :eager_load_job_artifacts, -> { includes(:job_artifacts) }
scope :eager_load_tags, -> { includes(:tags) }
- scope :eager_load_for_archiving_trace, -> { includes(:project, :pending_state) }
+ scope :eager_load_for_archiving_trace, -> { preload(:project, :pending_state) }
scope :eager_load_everything, -> do
includes(
@@ -180,7 +184,9 @@ module Ci
acts_as_taggable
- add_authentication_token_field :token, encrypted: :required
+ add_authentication_token_field :token,
+ encrypted: :required,
+ format_with_prefix: :partition_id_prefix_in_16_bit_encode
after_save :stick_build_if_status_changed
@@ -592,14 +598,21 @@ module Ci
.append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
.append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false, masked: true)
.append(key: 'CI_JOB_STARTED_AT', value: started_at&.iso8601)
- .append(key: 'CI_BUILD_ID', value: id.to_s)
- .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false, masked: true)
+
+ if Feature.disabled?(:ci_remove_legacy_predefined_variables, project)
+ variables
+ .append(key: 'CI_BUILD_ID', value: id.to_s)
+ .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false, masked: true)
+ end
+
+ variables
.append(key: 'CI_REGISTRY_USER', value: ::Gitlab::Auth::CI_JOB_USER)
.append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false, masked: true)
.append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false)
.concat(deploy_token_variables)
.concat(harbor_variables)
.concat(apple_app_store_variables)
+ .concat(google_play_variables)
end
end
@@ -650,6 +663,13 @@ module Ci
Gitlab::Ci::Variables::Collection.new(apple_app_store_integration.ci_variables)
end
+ def google_play_variables
+ return [] unless google_play_integration.try(:activated?)
+ return [] unless pipeline.protected_ref?
+
+ Gitlab::Ci::Variables::Collection.new(google_play_integration.ci_variables)
+ end
+
def features
{
trace_sections: true,
@@ -757,9 +777,7 @@ module Ci
end
def remove_token!
- if Feature.enabled?(:remove_job_token_on_completion, project)
- update!(token_encrypted: nil)
- end
+ update!(token_encrypted: nil)
end
# acts_as_taggable uses this method create/remove tags with contexts
@@ -802,7 +820,7 @@ module Ci
return unless project
return if user&.blocked?
- ActiveRecord::Associations::Preloader.new.preload([self], { runner: :tags })
+ ActiveRecord::Associations::Preloader.new(records: [self], associations: { runner: :tags }).call
project.execute_hooks(build_data.dup, :job_hooks) if project.has_active_hooks?(:job_hooks)
project.execute_integrations(build_data.dup, :job_hooks) if project.has_active_integrations?(:job_hooks)
@@ -902,9 +920,15 @@ module Ci
def cache
cache = Array.wrap(options[:cache])
+ cache.each do |single_cache|
+ single_cache[:fallback_keys] = [] unless single_cache.key?(:fallback_keys)
+ end
+
if project.jobs_cache_index
cache = cache.map do |single_cache|
- single_cache.merge(key: "#{single_cache[:key]}-#{project.jobs_cache_index}")
+ cache = single_cache.merge(key: "#{single_cache[:key]}-#{project.jobs_cache_index}")
+ fallback = cache.slice(:fallback_keys).transform_values { |keys| keys.map { |key| "#{key}-#{project.jobs_cache_index}" } }
+ cache.merge(fallback.compact)
end
end
@@ -913,10 +937,16 @@ module Ci
cache.map do |entry|
type_suffix = !entry[:unprotect] && pipeline.protected_ref? ? 'protected' : 'non_protected'
- entry.merge(key: "#{entry[:key]}-#{type_suffix}")
+ cache = entry.merge(key: "#{entry[:key]}-#{type_suffix}")
+ fallback = cache.slice(:fallback_keys).transform_values { |keys| keys.map { |key| "#{key}-#{type_suffix}" } }
+ cache.merge(fallback.compact)
end
end
+ def fallback_cache_keys_defined?
+ Array.wrap(options[:cache]).any? { |cache| cache[:fallback_keys].present? }
+ end
+
def credentials
Gitlab::Ci::Build::Credentials::Factory.new(self).create!
end
@@ -1091,10 +1121,6 @@ module Ci
::Ci::PendingBuild.upsert_from_build!(self)
end
- def create_runtime_metadata!
- ::Ci::RunningBuild.upsert_shared_runner_build!(self)
- end
-
##
# We can have only one queuing entry or running build tracking entry,
# because there is a unique index on `build_id` in each table, but we need
@@ -1161,11 +1187,6 @@ module Ci
end
end
- override :format_token
- def format_token(token)
- "#{partition_id.to_s(16)}_#{token}"
- end
-
protected
def run_status_commit_hooks!
@@ -1231,10 +1252,10 @@ module Ci
end
def job_jwt_variables
- if project.ci_cd_settings.opt_in_jwt?
+ if id_tokens?
id_tokens_variables
else
- predefined_jwt_variables.concat(id_tokens_variables)
+ predefined_jwt_variables
end
end
@@ -1251,8 +1272,6 @@ module Ci
end
def id_tokens_variables
- return [] unless id_tokens?
-
Gitlab::Ci::Variables::Collection.new.tap do |variables|
id_tokens.each do |var_name, token_data|
token = Gitlab::Ci::JwtV2.for_build(self, aud: token_data['aud'])
@@ -1308,6 +1327,10 @@ module Ci
).to_context]
)
end
+
+ def partition_id_prefix_in_16_bit_encode
+ "#{partition_id.to_s(16)}_"
+ end
end
end
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index b294afd405d..382f861a802 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -10,15 +10,16 @@ module Ci
include Presentable
include ChronicDurationAttribute
include Gitlab::Utils::StrongMemoize
+ include SafelyChangeColumnDefault
self.table_name = 'p_ci_builds_metadata'
self.primary_key = 'id'
+ columns_changing_default :partition_id
partitionable scope: :build
belongs_to :build, class_name: 'CommitStatus'
belongs_to :project
- belongs_to :runner_machine, class_name: 'Ci::RunnerMachine'
before_create :set_build_project
diff --git a/app/models/ci/build_need.rb b/app/models/ci/build_need.rb
index 03d1bd14bfb..940221619b3 100644
--- a/app/models/ci/build_need.rb
+++ b/app/models/ci/build_need.rb
@@ -6,8 +6,6 @@ module Ci
include BulkInsertSafe
include IgnorableColumns
- ignore_column :id_convert_to_bigint, remove_with: '16.0', remove_after: '2023-04-22'
-
belongs_to :build, class_name: "Ci::Processable", foreign_key: :build_id, inverse_of: :needs
partitionable scope: :build
diff --git a/app/models/ci/build_pending_state.rb b/app/models/ci/build_pending_state.rb
index 3684dac06c7..966884ae158 100644
--- a/app/models/ci/build_pending_state.rb
+++ b/app/models/ci/build_pending_state.rb
@@ -3,7 +3,7 @@
class Ci::BuildPendingState < Ci::ApplicationRecord
include Ci::Partitionable
- belongs_to :build, class_name: 'Ci::Build', foreign_key: :build_id
+ belongs_to :build, class_name: 'Ci::Build', foreign_key: :build_id, inverse_of: :pending_state
partitionable scope: :build
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index 541a8b5bffa..03b59b19ef1 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -9,7 +9,7 @@ module Ci
include ::Gitlab::ExclusiveLeaseHelpers
include ::Gitlab::OptimisticLocking
- belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id
+ belongs_to :build, class_name: "Ci::Build", foreign_key: :build_id, inverse_of: :trace_chunks
partitionable scope: :build
diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb
index 00cf1531483..4c76089617f 100644
--- a/app/models/ci/build_trace_metadata.rb
+++ b/app/models/ci/build_trace_metadata.rb
@@ -42,9 +42,7 @@ module Ci
end
def track_archival!(trace_artifact_id, checksum)
- update!(trace_artifact_id: trace_artifact_id,
- checksum: checksum,
- archived_at: Time.current)
+ update!(trace_artifact_id: trace_artifact_id, checksum: checksum, archived_at: Time.current)
end
def archival_attempts_message
diff --git a/app/models/ci/catalog/listing.rb b/app/models/ci/catalog/listing.rb
new file mode 100644
index 00000000000..b9e777f27a0
--- /dev/null
+++ b/app/models/ci/catalog/listing.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Ci
+ module Catalog
+ class Listing
+ # This class is the SSoT to displaying the list of resources in the
+ # CI/CD Catalog given a namespace as a scope.
+ # This model is not directly backed by a table and joins catalog resources
+ # with projects to return relevant data.
+ def initialize(namespace, current_user)
+ raise ArgumentError, 'Namespace is not a root namespace' unless namespace.root?
+
+ @namespace = namespace
+ @current_user = current_user
+ end
+
+ def resources
+ Ci::Catalog::Resource
+ .joins(:project).includes(:project)
+ .merge(projects_in_namespace_visible_to_user)
+ end
+
+ private
+
+ attr_reader :namespace, :current_user
+
+ def projects_in_namespace_visible_to_user
+ Project
+ .in_namespace(namespace.self_and_descendant_ids)
+ .public_or_visible_to_user(current_user, ::Gitlab::Access::DEVELOPER)
+ end
+ end
+ end
+end
diff --git a/app/models/ci/catalog/resource.rb b/app/models/ci/catalog/resource.rb
new file mode 100644
index 00000000000..bb4584aacae
--- /dev/null
+++ b/app/models/ci/catalog/resource.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Ci
+ module Catalog
+ # This class represents a CI/CD Catalog resource.
+ # A Catalog resource is normally associated to a project.
+ # This model connects to the `main` database because of its
+ # dependency on the Project model and its need to join with that table
+ # in order to generate the CI/CD catalog.
+ class Resource < ::ApplicationRecord
+ self.table_name = 'catalog_resources'
+
+ belongs_to :project
+
+ scope :for_projects, ->(project_ids) { where(project_id: project_ids) }
+
+ delegate :avatar_path, :description, :name, to: :project
+
+ def versions
+ project.releases.order_released_desc
+ end
+
+ def latest_version
+ versions.first
+ end
+ end
+ end
+end
diff --git a/app/models/ci/commit_with_pipeline.rb b/app/models/ci/commit_with_pipeline.rb
index dde4b534aaa..2aa249df321 100644
--- a/app/models/ci/commit_with_pipeline.rb
+++ b/app/models/ci/commit_with_pipeline.rb
@@ -19,7 +19,7 @@ class Ci::CommitWithPipeline < SimpleDelegator
end
def lazy_latest_pipeline
- BatchLoader.for(sha).batch do |shas, loader|
+ BatchLoader.for(sha).batch(key: project.id) do |shas, loader|
preload_pipelines = project.ci_pipelines.latest_pipeline_per_commit(shas.compact)
shas.each do |sha|
diff --git a/app/models/ci/daily_build_group_report_result.rb b/app/models/ci/daily_build_group_report_result.rb
index 598d1456a48..5ec54ee2983 100644
--- a/app/models/ci/daily_build_group_report_result.rb
+++ b/app/models/ci/daily_build_group_report_result.rb
@@ -4,9 +4,10 @@ module Ci
class DailyBuildGroupReportResult < Ci::ApplicationRecord
PARAM_TYPES = %w[coverage].freeze
- belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id
+ belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id,
+ inverse_of: :daily_build_group_report_results
belongs_to :project
- belongs_to :group
+ belongs_to :group, class_name: '::Group'
validates :data, json_schema: { filename: "daily_build_group_report_result_data" }
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index b03c46a164f..f04f0d27e51 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -29,5 +29,13 @@ module Ci
def audit_details
key
end
+
+ def group_name
+ group.name
+ end
+
+ def group_ci_cd_settings_path
+ Gitlab::Routing.url_helpers.group_settings_ci_cd_path(group)
+ end
end
end
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 89a3d269a43..766155c6a99 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -132,7 +132,7 @@ module Ci
PLAN_LIMIT_PREFIX = 'ci_max_artifact_size_'
belongs_to :project
- belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
+ belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id, inverse_of: :job_artifacts
mount_file_store_uploader JobArtifactUploader, skip_store_file: true
@@ -155,7 +155,7 @@ module Ci
scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) }
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
scope :for_job_ids, ->(job_ids) { where(job_id: job_ids) }
- scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) }
+ scope :for_job_name, ->(name) { joins(:job).merge(Ci::Build.by_name(name)) }
scope :created_at_before, ->(time) { where(arel_table[:created_at].lteq(time)) }
scope :id_before, ->(id) { where(arel_table[:id].lteq(id)) }
scope :id_after, ->(id) { where(arel_table[:id].gt(id)) }
@@ -177,6 +177,8 @@ module Ci
where(file_type: self.erasable_file_types)
end
+ scope :non_trace, -> { where.not(file_type: [:trace]) }
+
scope :downloadable, -> { where(file_type: DOWNLOADABLE_TYPES) }
scope :unlocked, -> { joins(job: :pipeline).merge(::Ci::Pipeline.unlocked) }
scope :order_expired_asc, -> { order(expire_at: :asc) }
diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb
index 20775077bd8..f389c642fd8 100644
--- a/app/models/ci/job_token/scope.rb
+++ b/app/models/ci/job_token/scope.rb
@@ -58,8 +58,7 @@ module Ci
end
def inbound_accessible?(accessed_project)
- # if the flag or setting is disabled any project is considered to be in scope.
- return true unless Feature.enabled?(:ci_inbound_job_token_scope, accessed_project)
+ # if the setting is disabled any project is considered to be in scope.
return true unless accessed_project.ci_inbound_job_token_scope_enabled?
inbound_linked_as_accessible?(accessed_project)
diff --git a/app/models/ci/job_variable.rb b/app/models/ci/job_variable.rb
index 998f0647ad5..573999995bc 100644
--- a/app/models/ci/job_variable.rb
+++ b/app/models/ci/job_variable.rb
@@ -7,7 +7,7 @@ module Ci
include Ci::RawVariable
include BulkInsertSafe
- belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id
+ belongs_to :job, class_name: "Ci::Build", foreign_key: :job_id, inverse_of: :job_variables
partitionable scope: :job
diff --git a/app/models/ci/namespace_mirror.rb b/app/models/ci/namespace_mirror.rb
index 5ea51fbe0a7..ff7e681217a 100644
--- a/app/models/ci/namespace_mirror.rb
+++ b/app/models/ci/namespace_mirror.rb
@@ -41,8 +41,7 @@ module Ci
namespace = event.namespace
traversal_ids = namespace.self_and_ancestor_ids(hierarchy_order: :desc)
- upsert({ namespace_id: event.namespace_id, traversal_ids: traversal_ids },
- unique_by: :namespace_id)
+ upsert({ namespace_id: event.namespace_id, traversal_ids: traversal_ids }, unique_by: :namespace_id)
end
end
end
diff --git a/app/models/ci/pending_build.rb b/app/models/ci/pending_build.rb
index 2b1eb67d4f2..14050a1e78e 100644
--- a/app/models/ci/pending_build.rb
+++ b/app/models/ci/pending_build.rb
@@ -14,7 +14,6 @@ module Ci
validates :namespace, presence: true
scope :ref_protected, -> { where(protected: true) }
- scope :queued_before, ->(time) { where(arel_table[:created_at].lt(time)) }
scope :with_instance_runners, -> { where(instance_runners_enabled: true) }
scope :for_tags, ->(tag_ids) do
if tag_ids.present?
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index bd426e02b9c..babea831d85 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -11,7 +11,6 @@ module Ci
include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize
include AtomicInternalId
- include EnumWithNil
include Ci::HasRef
include ShaAttribute
include FromUnion
@@ -19,6 +18,9 @@ module Ci
include EachBatch
include FastDestroyAll::Helpers
+ include IgnorableColumns
+ ignore_column :id_convert_to_bigint, remove_with: '16.3', remove_after: '2023-08-22'
+
MAX_OPEN_MERGE_REQUESTS_REFS = 4
PROJECT_ROUTE_AND_NAMESPACE_ROUTE = {
@@ -46,39 +48,53 @@ module Ci
belongs_to :project, inverse_of: :all_pipelines
belongs_to :user
- belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
+ belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline', inverse_of: :auto_canceled_pipelines
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
belongs_to :merge_request, class_name: 'MergeRequest'
belongs_to :external_pull_request
belongs_to :ci_ref, class_name: 'Ci::Ref', foreign_key: :ci_ref_id, inverse_of: :pipelines
has_internal_id :iid, scope: :project, presence: false,
- track_if: -> { !importing? },
- ensure_if: -> { !importing? },
- init: ->(pipeline, scope) do
- if pipeline
- pipeline.project&.all_pipelines&.maximum(:iid) || pipeline.project&.all_pipelines&.count
- elsif scope
- ::Ci::Pipeline.where(**scope).maximum(:iid)
- end
- end
+ track_if: -> { !importing? },
+ ensure_if: -> { !importing? },
+ init: ->(pipeline, scope) do
+ if pipeline
+ pipeline.project&.all_pipelines&.maximum(:iid) || pipeline.project&.all_pipelines&.count
+ elsif scope
+ ::Ci::Pipeline.where(**scope).maximum(:iid)
+ end
+ end
has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline
+
+ #
+ # In https://gitlab.com/groups/gitlab-org/-/epics/9991, we aim to convert all CommitStatus related models to
+ # Ci:Job models. With that epic, we aim to replace `statuses` with `jobs`.
+ #
+ # DEPRECATED:
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
+ has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
has_many :latest_statuses_ordered_by_stage, -> { latest.order(:stage_idx, :stage) }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :latest_statuses, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
- has_many :statuses_order_id_desc, -> { order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id
- has_many :processables, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
+ has_many :statuses_order_id_desc, -> { order_id_desc }, class_name: 'CommitStatus', foreign_key: :commit_id,
+ inverse_of: :pipeline
has_many :bridges, class_name: 'Ci::Bridge', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :generic_commit_statuses, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'GenericCommitStatus'
+ #
+ # NEW:
+ has_many :all_jobs, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
+ has_many :current_jobs, -> { latest }, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
+ has_many :all_processable_jobs, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
+ has_many :current_processable_jobs, -> { latest }, class_name: 'Ci::Processable', foreign_key: :commit_id, inverse_of: :pipeline
+
has_many :job_artifacts, through: :builds
has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks
- has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
+ has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id, inverse_of: :pipeline # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
has_many :latest_builds, -> { latest.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build'
has_many :downloadable_artifacts, -> do
- not_expired.or(where_exists(::Ci::Pipeline.artifacts_locked.where('ci_pipelines.id = ci_builds.commit_id'))).downloadable.with_job
+ not_expired.or(where_exists(Ci::Pipeline.artifacts_locked.where("#{Ci::Pipeline.quoted_table_name}.id = #{Ci::Build.quoted_table_name}.commit_id"))).downloadable.with_job
end, through: :latest_builds, source: :job_artifacts
has_many :latest_successful_builds, -> { latest.success.with_project_and_metadata }, foreign_key: :commit_id, inverse_of: :pipeline, class_name: 'Ci::Build'
@@ -86,17 +102,24 @@ module Ci
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
- has_many :merge_requests_as_head_pipeline, foreign_key: "head_pipeline_id", class_name: 'MergeRequest'
+ has_many :merge_requests_as_head_pipeline, foreign_key: :head_pipeline_id, class_name: 'MergeRequest',
+ inverse_of: :head_pipeline
+
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
- has_many :failed_builds, -> { latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
+ has_many :failed_builds, -> { latest.failed }, foreign_key: :commit_id, class_name: 'Ci::Build',
+ inverse_of: :pipeline
has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
- has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus'
+ has_many :cancelable_statuses, -> { cancelable }, foreign_key: :commit_id, class_name: 'CommitStatus',
+ inverse_of: :pipeline
has_many :manual_actions, -> { latest.manual_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
has_many :scheduled_actions, -> { latest.scheduled_actions.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build', inverse_of: :pipeline
- has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
- has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
- has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_pipeline_id
+ has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: :auto_canceled_by_id,
+ inverse_of: :auto_canceled_by
+ has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: :auto_canceled_by_id,
+ inverse_of: :auto_canceled_by
+ has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_pipeline_id,
+ inverse_of: :source_pipeline
has_one :source_pipeline, class_name: 'Ci::Sources::Pipeline', inverse_of: :pipeline
@@ -114,7 +137,9 @@ module Ci
has_one :pipeline_metadata, class_name: 'Ci::PipelineMetadata', inverse_of: :pipeline
- has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult', foreign_key: :last_pipeline_id
+ has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult',
+ foreign_key: :last_pipeline_id, inverse_of: :last_pipeline
+
has_many :latest_builds_report_results, through: :latest_builds, source: :report_results
has_many :pipeline_artifacts, class_name: 'Ci::PipelineArtifact', inverse_of: :pipeline, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -143,9 +168,9 @@ module Ci
# We use `Enums::Ci::Pipeline.sources` here so that EE can more easily extend
# this `Hash` with new values.
- enum_with_nil source: Enums::Ci::Pipeline.sources
+ enum source: Enums::Ci::Pipeline.sources
- enum_with_nil config_source: Enums::Ci::Pipeline.config_sources
+ enum config_source: Enums::Ci::Pipeline.config_sources
# We use `Enums::Ci::Pipeline.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values.
@@ -336,6 +361,22 @@ module Ci
AutoDevops::DisableWorker.perform_async(pipeline.id) if pipeline.auto_devops_source?
end
end
+
+ after_transition any => [:running, *::Ci::Pipeline.completed_statuses] do |pipeline|
+ project = pipeline&.project
+
+ next unless project
+ next unless Feature.enabled?(:pipeline_trigger_merge_status, project)
+
+ pipeline.run_after_commit do
+ next if pipeline.child?
+ next unless project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
+
+ pipeline.all_merge_requests.opened.each do |merge_request|
+ GraphqlTriggers.merge_request_merge_status_updated(merge_request)
+ end
+ end
+ end
end
scope :internal, -> { where(source: internal_sources) }
@@ -361,18 +402,25 @@ module Ci
scope :created_before_id, -> (id) { where(arel_table[:id].lt(id)) }
scope :before_pipeline, -> (pipeline) { created_before_id(pipeline.id).outside_pipeline_family(pipeline) }
scope :with_pipeline_source, -> (source) { where(source: source) }
+ scope :preload_pipeline_metadata, -> { preload(:pipeline_metadata) }
scope :outside_pipeline_family, ->(pipeline) do
where.not(id: pipeline.same_family_pipeline_ids)
end
scope :with_reports, -> (reports_scope) do
- where('EXISTS (?)', ::Ci::Build.latest.with_artifacts(reports_scope).where('ci_pipelines.id=ci_builds.commit_id').select(1))
+ where('EXISTS (?)',
+ ::Ci::Build
+ .latest
+ .with_artifacts(reports_scope)
+ .where("#{quoted_table_name}.id = #{Ci::Build.quoted_table_name}.commit_id")
+ .select(1)
+ )
end
scope :with_only_interruptible_builds, -> do
where('NOT EXISTS (?)',
- Ci::Build.where('ci_builds.commit_id = ci_pipelines.id')
+ Ci::Build.where("#{Ci::Build.quoted_table_name}.commit_id = #{quoted_table_name}.id")
.with_status(STARTED_STATUSES)
.not_interruptible
)
@@ -382,11 +430,15 @@ module Ci
# In general, please use `Ci::PipelinesForMergeRequestFinder` instead,
# for checking permission of the actor.
scope :triggered_by_merge_request, -> (merge_request) do
- where(source: :merge_request_event,
- merge_request: merge_request,
- project: [merge_request.source_project, merge_request.target_project])
+ where(
+ source: :merge_request_event,
+ merge_request: merge_request,
+ project: [merge_request.source_project, merge_request.target_project]
+ )
end
+ scope :order_id_desc, -> { order(id: :desc) }
+
# Returns the pipelines in descending order (= newest first), optionally
# limited to a number of references.
#
@@ -657,7 +709,7 @@ module Ci
# rubocop: enable CodeReuse/ServiceClass
def lazy_ref_commit
- BatchLoader.for(ref).batch do |refs, loader|
+ BatchLoader.for(ref).batch(key: project.id) do |refs, loader|
next unless project.repository_exists?
project.repository.list_commits_by_ref_name(refs).then do |commits|
@@ -818,8 +870,7 @@ module Ci
when 'manual' then block
when 'scheduled' then delay
else
- raise Ci::HasStatus::UnknownStatusError,
- "Unknown status `#{new_status}`"
+ raise Ci::HasStatus::UnknownStatusError, "Unknown status `#{new_status}`"
end
end
end
@@ -1282,7 +1333,7 @@ module Ci
types_to_collect = report_types.empty? ? ::Ci::JobArtifact::SECURITY_REPORT_FILE_TYPES : report_types
::Gitlab::Ci::Reports::Security::Reports.new(self).tap do |security_reports|
- latest_report_builds(reports_scope).each do |build|
+ latest_report_builds_in_self_and_project_descendants(reports_scope).includes(pipeline: { project: :route }).each do |build| # rubocop:disable Rails/FindEach
build.collect_security_reports!(security_reports, report_types: types_to_collect)
end
end
@@ -1294,7 +1345,7 @@ module Ci
def cluster_agent_authorizations
strong_memoize(:cluster_agent_authorizations) do
- ::Clusters::AgentAuthorizationsFinder.new(project).execute
+ ::Clusters::Agents::Authorizations::CiAccess::Finder.new(project).execute
end
end
diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb
index 20ff07e88ba..49d27053745 100644
--- a/app/models/ci/pipeline_schedule.rb
+++ b/app/models/ci/pipeline_schedule.rb
@@ -8,14 +8,15 @@ module Ci
include CronSchedulable
include Limitable
include EachBatch
+ include BatchNullifyDependentAssociations
self.limit_name = 'ci_pipeline_schedules'
self.limit_scope = :project
belongs_to :project
belongs_to :owner, class_name: 'User'
- has_one :last_pipeline, -> { order(id: :desc) }, class_name: 'Ci::Pipeline'
- has_many :pipelines
+ has_one :last_pipeline, -> { order(id: :desc) }, class_name: 'Ci::Pipeline', inverse_of: :pipeline_schedule
+ has_many :pipelines, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineScheduleVariable'
validates :cron, unless: :importing?, cron: true, presence: { unless: :importing? }
@@ -81,6 +82,14 @@ module Ci
def worker_cron_expression
Settings.cron_jobs['pipeline_schedule_worker']['cron']
end
+
+ # Using destroy instead of before_destroy as we want nullify_dependent_associations_in_batches
+ # to run first and not in a transaction block. This prevents timeouts for schedules with numerous pipelines
+ def destroy
+ nullify_dependent_associations_in_batches
+
+ super
+ end
end
end
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 8e83b41cd0b..f2457af0074 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -6,6 +6,9 @@ module Ci
include Ci::HasVariable
include Ci::RawVariable
+ include IgnorableColumns
+ ignore_column :id_convert_to_bigint, remove_with: '16.3', remove_after: '2023-08-22'
+
belongs_to :pipeline
partitionable scope: :pipeline
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index 37c82c125aa..4c421f066f9 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module Ci
+ # This class is a collection of common features between Ci::Build and Ci::Bridge.
+ # In https://gitlab.com/groups/gitlab-org/-/epics/9991, we aim to clarify class naming conventions.
class Processable < ::CommitStatus
include Gitlab::Utils::StrongMemoize
include FromUnion
diff --git a/app/models/ci/project_mirror.rb b/app/models/ci/project_mirror.rb
index 15a161d5b7c..23cd5d92730 100644
--- a/app/models/ci/project_mirror.rb
+++ b/app/models/ci/project_mirror.rb
@@ -13,8 +13,7 @@ module Ci
class << self
def sync!(event)
- upsert({ project_id: event.project_id, namespace_id: event.project.namespace_id },
- unique_by: :project_id)
+ upsert({ project_id: event.project_id, namespace_id: event.project.namespace_id }, unique_by: :project_id)
end
end
end
diff --git a/app/models/ci/ref.rb b/app/models/ci/ref.rb
index af5fdabff6e..199e1cd07e7 100644
--- a/app/models/ci/ref.rb
+++ b/app/models/ci/ref.rb
@@ -43,8 +43,7 @@ module Ci
class << self
def ensure_for(pipeline)
- safe_find_or_create_by(project_id: pipeline.project_id,
- ref_path: pipeline.source_ref_path)
+ safe_find_or_create_by(project_id: pipeline.project_id, ref_path: pipeline.source_ref_path)
end
def failing_state?(status_name)
diff --git a/app/models/ci/resource_group.rb b/app/models/ci/resource_group.rb
index b788e4f58c1..48f321a236d 100644
--- a/app/models/ci/resource_group.rb
+++ b/app/models/ci/resource_group.rb
@@ -29,13 +29,19 @@ module Ci
partition_id: processable.partition_id
}
- resources.free.limit(1).update_all(attrs) > 0
+ success = resources.free.limit(1).update_all(attrs) > 0
+ log_event(success: success, processable: processable, action: "assign resource to processable")
+
+ success
end
def release_resource_from(processable)
attrs = { build_id: nil, partition_id: nil }
- resources.retained_by(processable).update_all(attrs) > 0
+ success = resources.retained_by(processable).update_all(attrs) > 0
+ log_event(success: success, processable: processable, action: "release resource from processable")
+
+ success
end
def upcoming_processables
@@ -52,6 +58,10 @@ module Ci
end
end
+ def current_processable
+ Ci::Processable.find_by('(id, partition_id) IN (?)', resources.select('build_id, partition_id'))
+ end
+
private
# In order to avoid deadlock, we do NOT specify the job execution order in the same pipeline.
@@ -72,5 +82,14 @@ module Ci
# belong to the same resource group are executed once at time.
self.resources.build if self.resources.empty?
end
+
+ def log_event(success:, processable:, action:)
+ Gitlab::Ci::ResourceGroups::Logger.build.info({
+ resource_group_id: self.id,
+ processable_id: processable.id,
+ message: "attempted to #{action}",
+ success: success
+ })
+ end
end
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 09ac0fa69e7..7727e94875b 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -17,7 +17,10 @@ module Ci
extend ::Gitlab::Utils::Override
- add_authentication_token_field :token, encrypted: :optional, expires_at: :compute_token_expiration
+ add_authentication_token_field :token,
+ encrypted: :optional,
+ expires_at: :compute_token_expiration,
+ format_with_prefix: :prefix_for_new_and_legacy_runner
enum access_level: {
not_protected: 0,
@@ -54,6 +57,9 @@ module Ci
# The `STALE_TIMEOUT` constant defines the how far past the last contact or creation date a runner will be considered stale
STALE_TIMEOUT = 3.months
+ # Only allow authentication token to be visible for a short while
+ REGISTRATION_AVAILABILITY_TIME = 1.hour
+
AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze
AVAILABLE_TYPES = runner_types.keys.freeze
AVAILABLE_STATUSES = %w[active paused online offline never_contacted stale].freeze # TODO: Remove in %16.0: active, paused. Relevant issue: https://gitlab.com/gitlab-org/gitlab/-/issues/344648
@@ -64,7 +70,7 @@ module Ci
TAG_LIST_MAX_LENGTH = 50
- has_many :runner_machines, inverse_of: :runner
+ has_many :runner_managers, inverse_of: :runner
has_many :builds
has_many :runner_projects, inverse_of: :runner, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :runner_projects, disable_joins: true
@@ -81,8 +87,13 @@ module Ci
scope :active, -> (value = true) { where(active: value) }
scope :paused, -> { active(false) }
scope :online, -> { where('contacted_at > ?', online_contact_time_deadline) }
- scope :recent, -> { where('ci_runners.created_at >= :date OR ci_runners.contacted_at >= :date', date: stale_deadline) }
- scope :stale, -> { where('ci_runners.created_at < :date AND (ci_runners.contacted_at IS NULL OR ci_runners.contacted_at < :date)', date: stale_deadline) }
+ scope :recent, -> do
+ where('ci_runners.created_at >= :datetime OR ci_runners.contacted_at >= :datetime', datetime: stale_deadline)
+ end
+ scope :stale, -> do
+ where('ci_runners.created_at <= :datetime AND ' \
+ '(ci_runners.contacted_at IS NULL OR ci_runners.contacted_at <= :datetime)', datetime: stale_deadline)
+ end
scope :offline, -> { where(arel_table[:contacted_at].lteq(online_contact_time_deadline)) }
scope :never_contacted, -> { where(contacted_at: nil) }
scope :ordered, -> { order(id: :desc) }
@@ -123,7 +134,7 @@ module Ci
belonging_to_group(group_self_and_ancestors_ids)
}
- scope :belonging_to_parent_group_of_project, -> (project_id) {
+ scope :belonging_to_parent_groups_of_project, -> (project_id) {
raise ArgumentError, "only 1 project_id allowed for performance reasons" unless project_id.is_a?(Integer)
project_groups = ::Group.joins(:projects).where(projects: { id: project_id })
@@ -137,7 +148,7 @@ module Ci
from_union(
[
belonging_to_project(project_id),
- project.group_runners_enabled? ? belonging_to_parent_group_of_project(project_id) : nil,
+ project.group_runners_enabled? ? belonging_to_parent_groups_of_project(project_id) : nil,
project.shared_runners
].compact,
remove_duplicates: false
@@ -185,6 +196,7 @@ module Ci
scope :order_token_expires_at_asc, -> { order(token_expires_at: :asc) }
scope :order_token_expires_at_desc, -> { order(token_expires_at: :desc) }
scope :with_tags, -> { preload(:tags) }
+ scope :with_creator, -> { preload(:creator) }
validate :tag_constraints
validates :access_level, presence: true
@@ -203,16 +215,14 @@ module Ci
cached_attr_reader :version, :revision, :platform, :architecture, :ip_address, :contacted_at, :executor_type
chronic_duration_attr :maximum_timeout_human_readable, :maximum_timeout,
- error_message: 'Maximum job timeout has a value which could not be accepted'
+ error_message: 'Maximum job timeout has a value which could not be accepted'
validates :maximum_timeout, allow_nil: true,
- numericality: { greater_than_or_equal_to: 600,
- message: 'needs to be at least 10 minutes' }
+ numericality: { greater_than_or_equal_to: 600, message: 'needs to be at least 10 minutes' }
validates :public_projects_minutes_cost_factor, :private_projects_minutes_cost_factor,
allow_nil: false,
- numericality: { greater_than_or_equal_to: 0.0,
- message: 'needs to be non-negative' }
+ numericality: { greater_than_or_equal_to: 0.0, message: 'needs to be non-negative' }
validates :config, json_schema: { filename: 'ci_runner_config' }
@@ -332,15 +342,10 @@ module Ci
def stale?
return false unless created_at
- [created_at, contacted_at].compact.max < self.class.stale_deadline
+ [created_at, contacted_at].compact.max <= self.class.stale_deadline
end
- def status(legacy_mode = nil)
- # TODO Deprecate legacy_mode in %16.0 and make it a no-op
- # (see https://gitlab.com/gitlab-org/gitlab/-/issues/360545)
- # TODO Remove legacy_mode in %17.0
- return deprecated_rest_status if legacy_mode == '14.5'
-
+ def status
return :stale if stale?
return :never_contacted unless contacted_at
@@ -434,7 +439,7 @@ module Ci
ensure_runner_queue_value == value if value.present?
end
- def heartbeat(values)
+ def heartbeat(values, update_contacted_at: true)
##
# We can safely ignore writes performed by a runner heartbeat. We do
# not want to upgrade database connection proxy to use the primary
@@ -442,20 +447,18 @@ module Ci
#
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config, :executor) || {}
- values[:contacted_at] = Time.current
+ values[:contacted_at] = Time.current if update_contacted_at
if values.include?(:executor)
values[:executor_type] = EXECUTOR_NAME_TO_TYPES.fetch(values.delete(:executor), :unknown)
end
- cache_attributes(values)
+ new_version = values[:version]
+ schedule_runner_version_update(new_version) if new_version && values[:version] != version
- # We save data without validation, it will always change due to `contacted_at`
- if persist_cached_data?
- version_updated = values.include?(:version) && values[:version] != version
+ merge_cache_attributes(values)
- update_columns(values)
- schedule_runner_version_update if version_updated
- end
+ # We save data without validation, it will always change due to `contacted_at`
+ update_columns(values) if persist_cached_data?
end
end
@@ -488,15 +491,18 @@ module Ci
end
end
- override :format_token
- def format_token(token)
- return token if registration_token_registration_type?
+ def ensure_manager(system_xid, &blk)
+ RunnerManager.safe_find_or_create_by!(runner_id: id, system_xid: system_xid.to_s, &blk) # rubocop: disable Performance/ActiveRecordSubtransactionMethods
+ end
- "#{CREATED_RUNNER_TOKEN_PREFIX}#{token}"
+ def registration_available?
+ authenticated_user_registration_type? &&
+ created_at > REGISTRATION_AVAILABILITY_TIME.ago &&
+ !runner_managers.any?
end
- def ensure_machine(system_xid, &blk)
- RunnerMachine.safe_find_or_create_by!(runner_id: id, system_xid: system_xid.to_s, &blk) # rubocop: disable Performance/ActiveRecordSubtransactionMethods
+ def gitlab_hosted?
+ Gitlab.com? && instance_type?
end
private
@@ -586,7 +592,7 @@ module Ci
end
def exactly_one_group
- unless runner_namespaces.one?
+ unless runner_namespaces.size == 1
errors.add(:runner, 'needs to be assigned to exactly one group')
end
end
@@ -594,10 +600,16 @@ module Ci
# TODO Remove in 16.0 when runners are known to send a system_id
# For now, heartbeats with version updates might result in two Sidekiq jobs being queued if a runner has a system_id
# This is not a problem since the jobs are deduplicated on the version
- def schedule_runner_version_update
- return unless version
+ def schedule_runner_version_update(new_version)
+ return unless new_version && Gitlab::Ci::RunnerReleases.instance.enabled?
+
+ Ci::Runners::ProcessRunnerVersionUpdateWorker.perform_async(new_version)
+ end
+
+ def prefix_for_new_and_legacy_runner
+ return if registration_token_registration_type?
- Ci::Runners::ProcessRunnerVersionUpdateWorker.perform_async(version)
+ CREATED_RUNNER_TOKEN_PREFIX
end
end
end
diff --git a/app/models/ci/runner_machine.rb b/app/models/ci/runner_manager.rb
index e52659a011f..e36024d9f5b 100644
--- a/app/models/ci/runner_machine.rb
+++ b/app/models/ci/runner_manager.rb
@@ -1,23 +1,23 @@
# frozen_string_literal: true
module Ci
- class RunnerMachine < Ci::ApplicationRecord
+ class RunnerManager < Ci::ApplicationRecord
include FromUnion
include RedisCacheable
include Ci::HasRunnerExecutor
- include IgnorableColumns
- ignore_column :machine_xid, remove_with: '15.11', remove_after: '2022-03-22'
+ # For legacy reasons, the table name is ci_runner_machines in the database
+ self.table_name = 'ci_runner_machines'
# The `UPDATE_CONTACT_COLUMN_EVERY` defines how often the Runner Machine DB entry can be updated
- UPDATE_CONTACT_COLUMN_EVERY = 40.minutes..55.minutes
+ UPDATE_CONTACT_COLUMN_EVERY = (40.minutes)..(55.minutes)
belongs_to :runner
- has_many :build_metadata, class_name: 'Ci::BuildMetadata'
- has_many :builds, through: :build_metadata, class_name: 'Ci::Build'
- belongs_to :runner_version, inverse_of: :runner_machines, primary_key: :version, foreign_key: :version,
- class_name: 'Ci::RunnerVersion'
+ has_many :runner_manager_builds, inverse_of: :runner_manager, class_name: 'Ci::RunnerManagerBuild'
+ has_many :builds, through: :runner_manager_builds, class_name: 'Ci::Build'
+ belongs_to :runner_version, inverse_of: :runner_managers, primary_key: :version, foreign_key: :version,
+ class_name: 'Ci::RunnerVersion'
validates :runner, presence: true
validates :system_xid, presence: true, length: { maximum: 64 }
@@ -30,7 +30,7 @@ module Ci
cached_attr_reader :version, :revision, :platform, :architecture, :ip_address, :contacted_at, :executor_type
- # The `STALE_TIMEOUT` constant defines the how far past the last contact or creation date a runner machine
+ # The `STALE_TIMEOUT` constant defines the how far past the last contact or creation date a runner manager
# will be considered stale
STALE_TIMEOUT = 7.days
@@ -44,7 +44,15 @@ module Ci
remove_duplicates: false).where(created_some_time_ago)
end
- def heartbeat(values)
+ def self.online_contact_time_deadline
+ Ci::Runner.online_contact_time_deadline
+ end
+
+ def self.stale_deadline
+ STALE_TIMEOUT.ago
+ end
+
+ def heartbeat(values, update_contacted_at: true)
##
# We can safely ignore writes performed by a runner heartbeat. We do
# not want to upgrade database connection proxy to use the primary
@@ -52,24 +60,40 @@ module Ci
#
::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config, :executor) || {}
- values[:contacted_at] = Time.current
+ values[:contacted_at] = Time.current if update_contacted_at
if values.include?(:executor)
values[:executor_type] = Ci::Runner::EXECUTOR_NAME_TO_TYPES.fetch(values.delete(:executor), :unknown)
end
- version_changed = values.include?(:version) && values[:version] != version
+ new_version = values[:version]
+ schedule_runner_version_update(new_version) if new_version && values[:version] != version
- cache_attributes(values)
-
- schedule_runner_version_update if version_changed
+ merge_cache_attributes(values)
# We save data without validation, it will always change due to `contacted_at`
update_columns(values) if persist_cached_data?
end
end
+ def status
+ return :stale if stale?
+ return :never_contacted unless contacted_at
+
+ online? ? :online : :offline
+ end
+
private
+ def online?
+ contacted_at && contacted_at > self.class.online_contact_time_deadline
+ end
+
+ def stale?
+ return false unless created_at
+
+ [created_at, contacted_at].compact.max <= self.class.stale_deadline
+ end
+
def persist_cached_data?
# Use a random threshold to prevent beating DB updates.
contacted_at_max_age = Random.rand(UPDATE_CONTACT_COLUMN_EVERY)
@@ -79,10 +103,10 @@ module Ci
(Time.current - real_contacted_at) >= contacted_at_max_age
end
- def schedule_runner_version_update
- return unless version
+ def schedule_runner_version_update(new_version)
+ return unless new_version && Gitlab::Ci::RunnerReleases.instance.enabled?
- Ci::Runners::ProcessRunnerVersionUpdateWorker.perform_async(version)
+ Ci::Runners::ProcessRunnerVersionUpdateWorker.perform_async(new_version)
end
end
end
diff --git a/app/models/ci/runner_manager_build.rb b/app/models/ci/runner_manager_build.rb
new file mode 100644
index 00000000000..322c5ae3a68
--- /dev/null
+++ b/app/models/ci/runner_manager_build.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Ci
+ class RunnerManagerBuild < Ci::ApplicationRecord
+ include Ci::Partitionable
+
+ self.table_name = :p_ci_runner_machine_builds
+ self.primary_key = :build_id
+
+ partitionable scope: :build, partitioned: true
+
+ alias_attribute :runner_manager_id, :runner_machine_id
+
+ belongs_to :build, inverse_of: :runner_manager_build, class_name: 'Ci::Build'
+ belongs_to :runner_manager, foreign_key: :runner_machine_id, inverse_of: :runner_manager_builds,
+ class_name: 'Ci::RunnerManager'
+
+ validates :build, presence: true
+ validates :runner_manager, presence: true
+
+ scope :for_build, ->(build_id) { where(build_id: build_id) }
+
+ def self.pluck_build_id_and_runner_manager_id
+ select(:build_id, :runner_manager_id)
+ .pluck(:build_id, :runner_manager_id)
+ .to_h
+ end
+ end
+end
diff --git a/app/models/ci/runner_version.rb b/app/models/ci/runner_version.rb
index ec42f46b165..03b50f13989 100644
--- a/app/models/ci/runner_version.rb
+++ b/app/models/ci/runner_version.rb
@@ -3,9 +3,8 @@
module Ci
class RunnerVersion < Ci::ApplicationRecord
include EachBatch
- include EnumWithNil
- enum_with_nil status: {
+ enum status: {
not_processed: nil,
invalid_version: -1,
unavailable: 1,
@@ -20,7 +19,7 @@ module Ci
recommended: 'Upgrade is available and recommended for the runner.'
}.freeze
- has_many :runner_machines, inverse_of: :runner_version, foreign_key: :version, class_name: 'Ci::RunnerMachine'
+ has_many :runner_managers, inverse_of: :runner_version, foreign_key: :version, class_name: 'Ci::RunnerManager'
# This scope returns all versions that might need recalculating. For instance, once a version is considered
# :recommended, it normally doesn't change status even if the instance is upgraded
diff --git a/app/models/ci/running_build.rb b/app/models/ci/running_build.rb
index 43214b0c336..e6f80658f5d 100644
--- a/app/models/ci/running_build.rb
+++ b/app/models/ci/running_build.rb
@@ -24,10 +24,12 @@ module Ci
raise ArgumentError, 'build has not been picked by a shared runner'
end
- entry = self.new(build: build,
- project: build.project,
- runner: build.runner,
- runner_type: build.runner.runner_type)
+ entry = self.new(
+ build: build,
+ project: build.project,
+ runner: build.runner,
+ runner_type: build.runner.runner_type
+ )
entry.validate!
diff --git a/app/models/ci/sources/pipeline.rb b/app/models/ci/sources/pipeline.rb
index 855e68d1db1..719d19f4169 100644
--- a/app/models/ci/sources/pipeline.rb
+++ b/app/models/ci/sources/pipeline.rb
@@ -10,6 +10,7 @@ module Ci
belongs_to :project, class_name: "::Project"
belongs_to :pipeline, class_name: "Ci::Pipeline", inverse_of: :source_pipeline
+ belongs_to :build, class_name: "Ci::Build", foreign_key: :source_job_id, inverse_of: :sourced_pipelines
belongs_to :source_project, class_name: "::Project", foreign_key: :source_project_id
belongs_to :source_job, class_name: "CommitStatus", foreign_key: :source_job_id
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 46a9e3f6494..d61760bd0fc 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -27,6 +27,7 @@ module Ci
has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id, inverse_of: :ci_stage
has_many :builds, foreign_key: :stage_id, inverse_of: :ci_stage
has_many :bridges, foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :generic_commit_statuses, foreign_key: :stage_id, inverse_of: :ci_stage
scope :ordered, -> { order(position: :asc) }
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
@@ -111,12 +112,12 @@ module Ci
when 'scheduled' then delay
when 'skipped', nil then skip
else
- raise Ci::HasStatus::UnknownStatusError,
- "Unknown status `#{new_status}`"
+ raise Ci::HasStatus::UnknownStatusError, "Unknown status `#{new_status}`"
end
end
end
+ # This will be removed with ci_remove_ensure_stage_service
def update_legacy_status
set_status(latest_stage_status.to_s)
end
@@ -150,6 +151,7 @@ module Ci
blocked? || skipped?
end
+ # This will be removed with ci_remove_ensure_stage_service
def latest_stage_status
statuses.latest.composite_status || 'skipped'
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 1b2a7dc3fe4..58da1b4bd7e 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -8,7 +8,7 @@ module Ci
TRIGGER_TOKEN_PREFIX = 'glptt-'
- ignore_column :ref, remove_with: '15.4', remove_after: '2022-08-22'
+ ignore_column :ref, remove_with: '16.1', remove_after: '2023-05-22'
self.limit_name = 'pipeline_triggers'
self.limit_scope = :project
@@ -26,8 +26,7 @@ module Ci
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32,
- encode: false,
- encode_vi: false
+ encode: false
before_validation :set_default_values