summaryrefslogtreecommitdiff
path: root/app/models/ci
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/ci')
-rw-r--r--app/models/ci/build.rb47
-rw-r--r--app/models/ci/build_report_result.rb4
-rw-r--r--app/models/ci/group.rb3
-rw-r--r--app/models/ci/group_variable.rb4
-rw-r--r--app/models/ci/job_artifact.rb2
-rw-r--r--app/models/ci/legacy_stage.rb73
-rw-r--r--app/models/ci/pending_build.rb14
-rw-r--r--app/models/ci/pipeline.rb85
-rw-r--r--app/models/ci/pipeline_artifact.rb17
-rw-r--r--app/models/ci/runner.rb20
-rw-r--r--app/models/ci/runner_version.rb34
-rw-r--r--app/models/ci/stage.rb2
-rw-r--r--app/models/ci/trigger.rb3
-rw-r--r--app/models/ci/variable.rb4
14 files changed, 143 insertions, 169 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index e35198ba31f..7f9697d0424 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -2,6 +2,7 @@
module Ci
class Build < Ci::Processable
+ prepend Ci::BulkInsertableTags
include Ci::Metadatable
include Ci::Contextable
include TokenAuthenticatable
@@ -14,8 +15,6 @@ module Ci
extend ::Gitlab::Utils::Override
- BuildArchivedError = Class.new(StandardError)
-
belongs_to :project, inverse_of: :builds
belongs_to :runner
belongs_to :trigger_request
@@ -30,10 +29,6 @@ module Ci
return_exit_code: -> (build) { build.exit_codes_defined? }
}.freeze
- DEFAULT_RETRIES = {
- scheduler_failure: 2
- }.freeze
-
DEGRADATION_THRESHOLD_VARIABLE_NAME = 'DEGRADATION_THRESHOLD'
RUNNERS_STATUS_CACHE_EXPIRATION = 1.minute
@@ -172,7 +167,6 @@ module Ci
end
scope :with_artifacts_not_expired, -> { with_downloadable_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.current) }
- scope :with_expired_artifacts, -> { with_downloadable_artifacts.where('artifacts_expire_at < ?', Time.current) }
scope :with_pipeline_locked_artifacts, -> { joins(:pipeline).where('pipeline.locked': Ci::Pipeline.lockeds[:artifacts_locked]) }
scope :last_month, -> { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, -> { where(when: :manual, status: COMPLETED_STATUSES + %i[manual]) }
@@ -187,13 +181,6 @@ module Ci
joins(:metadata).where("ci_builds_metadata.config_options -> 'artifacts' -> 'reports' ?| array[:job_types]", job_types: job_types)
end
- scope :queued_before, ->(time) { where(arel_table[:queued_at].lt(time)) }
-
- scope :preload_project_and_pipeline_project, -> do
- preload(Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE,
- pipeline: Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE)
- end
-
scope :with_coverage, -> { where.not(coverage: nil) }
scope :without_coverage, -> { where(coverage: nil) }
scope :with_coverage_regex, -> { where.not(coverage_regex: nil) }
@@ -207,7 +194,7 @@ module Ci
after_save :stick_build_if_status_changed
after_create unless: :importing? do |build|
- run_after_commit { BuildHooksWorker.perform_async(build.id) }
+ run_after_commit { BuildHooksWorker.perform_async(build) }
end
class << self
@@ -217,10 +204,6 @@ module Ci
ActiveModel::Name.new(self, nil, 'job')
end
- def first_pending
- pending.unstarted.order('created_at ASC').first
- end
-
def with_preloads
preload(:job_artifacts_archive, :job_artifacts, :tags, project: [:namespace])
end
@@ -302,7 +285,7 @@ module Ci
build.run_after_commit do
BuildQueueWorker.perform_async(id)
- BuildHooksWorker.perform_async(id)
+ BuildHooksWorker.perform_async(build)
end
end
@@ -330,7 +313,7 @@ module Ci
build.run_after_commit do
build.ensure_persistent_ref
- BuildHooksWorker.perform_async(id)
+ BuildHooksWorker.perform_async(build)
end
end
@@ -338,11 +321,7 @@ module Ci
build.run_after_commit do
build.run_status_commit_hooks!
- if Feature.enabled?(:ci_build_finished_worker_namespace_changed, build.project)
- Ci::BuildFinishedWorker.perform_async(id)
- else
- ::BuildFinishedWorker.perform_async(id)
- end
+ Ci::BuildFinishedWorker.perform_async(id)
end
end
@@ -446,10 +425,6 @@ module Ci
true
end
- def save_tags
- super unless Thread.current['ci_bulk_insert_tags']
- end
-
def archived?
return true if degenerated?
@@ -556,10 +531,6 @@ module Ci
self.options.dig(:environment, :deployment_tier) if self.options
end
- def outdated_deployment?
- success? && !deployment.try(:last?)
- end
-
def triggered_by?(current_user)
user == current_user
end
@@ -1162,6 +1133,14 @@ module Ci
Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id) if user_id.present? && count_user_deployment?
end
+ def track_verify_usage
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id) if user_id.present? && count_user_verification?
+ end
+
+ def count_user_verification?
+ has_environment? && environment_action == 'verify'
+ end
+
def each_report(report_types)
job_artifacts_for_types(report_types).each do |report_artifact|
report_artifact.each_blob do |blob|
diff --git a/app/models/ci/build_report_result.rb b/app/models/ci/build_report_result.rb
index 2c08fc4c8bf..b674c1b1a0e 100644
--- a/app/models/ci/build_report_result.rb
+++ b/app/models/ci/build_report_result.rb
@@ -39,9 +39,5 @@ module Ci
def suite_error
tests.dig("suite_error")
end
-
- def tests_total
- [tests_success, tests_failed, tests_errored, tests_skipped].sum
- end
end
end
diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb
index e5cb2026503..0105366d99b 100644
--- a/app/models/ci/group.rb
+++ b/app/models/ci/group.rb
@@ -50,8 +50,7 @@ module Ci
def status_struct
strong_memoize(:status_struct) do
- Gitlab::Ci::Status::Composite
- .new(@jobs, project: project)
+ Gitlab::Ci::Status::Composite.new(@jobs)
end
end
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index 0af5533613f..e11edbda6dc 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -19,5 +19,9 @@ module Ci
scope :unprotected, -> { where(protected: false) }
scope :by_environment_scope, -> (environment_scope) { where(environment_scope: environment_scope) }
scope :for_groups, ->(group_ids) { where(group_id: group_ids) }
+
+ def audit_details
+ key
+ end
end
end
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 81943cfa651..ee7175a4f69 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -322,7 +322,7 @@ module Ci
def expire_in=(value)
self.expire_at =
if value
- ::Gitlab::Ci::Build::Artifacts::ExpireInParser.new(value).seconds_from_now
+ ::Gitlab::Ci::Build::DurationParser.new(value).seconds_from_now
end
end
diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb
deleted file mode 100644
index ffd3d3fcd88..00000000000
--- a/app/models/ci/legacy_stage.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- # Currently this is artificial object, constructed dynamically
- # We should migrate this object to actual database record in the future
- class LegacyStage
- include StaticModel
- include Presentable
-
- attr_reader :pipeline, :name
-
- delegate :project, to: :pipeline
-
- def initialize(pipeline, name:, status: nil, warnings: nil)
- @pipeline = pipeline
- @name = name
- @status = status
- # support ints and booleans
- @has_warnings = ActiveRecord::Type::Boolean.new.cast(warnings)
- end
-
- def groups
- @groups ||= Ci::Group.fabricate(project, self)
- end
-
- def to_param
- name
- end
-
- def statuses_count
- @statuses_count ||= statuses.count
- end
-
- def status
- @status ||= statuses.latest.composite_status(project: project)
- end
-
- def detailed_status(current_user)
- Gitlab::Ci::Status::Stage::Factory
- .new(self, current_user)
- .fabricate!
- end
-
- def latest_statuses
- statuses.ordered.latest
- end
-
- def statuses
- @statuses ||= pipeline.statuses.where(stage: name)
- end
-
- def builds
- @builds ||= pipeline.builds.where(stage: name)
- end
-
- def success?
- status.to_s == 'success'
- end
-
- def has_warnings?
- # lazilly calculate the warnings
- if @has_warnings.nil?
- @has_warnings = statuses.latest.failed_but_allowed.any?
- end
-
- @has_warnings
- end
-
- def manual_playable?
- %[manual scheduled skipped].include?(status.to_s)
- end
- end
-end
diff --git a/app/models/ci/pending_build.rb b/app/models/ci/pending_build.rb
index d900a056242..0fa6a234a3d 100644
--- a/app/models/ci/pending_build.rb
+++ b/app/models/ci/pending_build.rb
@@ -30,10 +30,6 @@ module Ci
self.upsert(entry.attributes.compact, returning: %w[build_id], unique_by: :build_id)
end
- def maintain_denormalized_data?
- ::Feature.enabled?(:ci_pending_builds_maintain_denormalized_data)
- end
-
private
def args_from_build(build)
@@ -43,13 +39,13 @@ module Ci
build: build,
project: project,
protected: build.protected?,
- namespace: project.namespace
+ namespace: project.namespace,
+ tag_ids: build.tags_ids,
+ instance_runners_enabled: shared_runners_enabled?(project)
}
- if maintain_denormalized_data?
- args.store(:tag_ids, build.tags_ids)
- args.store(:instance_runners_enabled, shared_runners_enabled?(project))
- args.store(:namespace_traversal_ids, project.namespace.traversal_ids) if group_runners_enabled?(project)
+ if group_runners_enabled?(project)
+ args.store(:namespace_traversal_ids, project.namespace.traversal_ids)
end
args
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 5d316906bd3..78b55680b5e 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -27,8 +27,6 @@ module Ci
DEFAULT_CONFIG_PATH = CONFIG_EXTENSION
CANCELABLE_STATUSES = (Ci::HasStatus::CANCELABLE_STATUSES + ['manual']).freeze
- BridgeStatusError = Class.new(StandardError)
-
paginates_per 15
sha_attribute :source_sha
@@ -133,6 +131,7 @@ module Ci
validates :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create
after_create :keep_around_commits, unless: :importing?
+ after_find :observe_age_in_minutes, unless: :importing?
use_fast_destroy :job_artifacts
use_fast_destroy :build_trace_chunks
@@ -241,6 +240,13 @@ module Ci
pipeline.run_after_commit do
unless pipeline.user&.blocked?
+ Gitlab::AppLogger.info(
+ message: "Enqueuing hooks for Pipeline #{pipeline.id}: #{pipeline.status}",
+ class: self.class.name,
+ pipeline_id: pipeline.id,
+ project_id: pipeline.project_id,
+ pipeline_status: pipeline.status)
+
PipelineHooksWorker.perform_async(pipeline.id)
end
@@ -332,8 +338,8 @@ module Ci
scope :for_id, -> (id) { where(id: id) }
scope :for_iid, -> (iid) { where(iid: iid) }
scope :for_project, -> (project_id) { where(project_id: project_id) }
- scope :created_after, -> (time) { where('ci_pipelines.created_at > ?', time) }
- scope :created_before_id, -> (id) { where('ci_pipelines.id < ?', id) }
+ scope :created_after, -> (time) { where(arel_table[:created_at].gt(time)) }
+ 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) }
@@ -490,40 +496,16 @@ module Ci
.pluck(:stage, :stage_idx).map(&:first)
end
- def legacy_stage(name)
- stage = Ci::LegacyStage.new(self, name: name)
- stage unless stage.statuses_count == 0
- end
-
def ref_exists?
project.repository.ref_exists?(git_ref)
rescue Gitlab::Git::Repository::NoRepository
false
end
- def legacy_stages_using_composite_status
- stages = latest_statuses_ordered_by_stage.group_by(&:stage)
-
- stages.map do |stage_name, jobs|
- composite_status = Gitlab::Ci::Status::Composite
- .new(jobs)
-
- Ci::LegacyStage.new(self,
- name: stage_name,
- status: composite_status.status,
- warnings: composite_status.warnings?)
- end
- end
-
def triggered_pipelines_with_preloads
triggered_pipelines.preload(:source_job)
end
- # TODO: Remove usage of this method in templates
- def legacy_stages
- legacy_stages_using_composite_status
- end
-
def valid_commit_sha
if self.sha == Gitlab::Git::BLANK_SHA
self.errors.add(:sha, " cant be 00000000 (branch removal)")
@@ -1004,6 +986,10 @@ module Ci
object_hierarchy(project_condition: :same).base_and_descendants
end
+ def self_and_descendants_complete?
+ self_and_descendants.all?(&:complete?)
+ end
+
# Follow the parent-child relationships and return the top-level parent
def root_ancestor
return self unless child?
@@ -1078,7 +1064,11 @@ module Ci
end
def has_reports?(reports_scope)
- complete? && latest_report_builds(reports_scope).exists?
+ if Feature.enabled?(:mr_show_reports_immediately, project, type: :development)
+ latest_report_builds(reports_scope).exists?
+ else
+ complete? && latest_report_builds(reports_scope).exists?
+ end
end
def has_coverage_reports?
@@ -1100,7 +1090,7 @@ module Ci
end
def test_reports
- Gitlab::Ci::Reports::TestReports.new.tap do |test_reports|
+ Gitlab::Ci::Reports::TestReport.new.tap do |test_reports|
latest_test_report_builds.find_each do |build|
build.collect_test_reports!(test_reports)
end
@@ -1222,6 +1212,10 @@ module Ci
Gitlab::Utils.slugify(source_ref.to_s)
end
+ def stage(name)
+ stages.find_by(name: name)
+ end
+
def find_stage_by_name!(name)
stages.find_by!(name: name)
end
@@ -1307,10 +1301,20 @@ module Ci
end
end
- def has_expired_test_reports?
- strong_memoize(:has_expired_test_reports) do
- has_reports?(::Ci::JobArtifact.test_reports.expired)
+ def has_test_reports?
+ strong_memoize(:has_test_reports) do
+ has_reports?(::Ci::JobArtifact.test_reports)
+ end
+ end
+
+ def age_in_minutes
+ return 0 unless persisted?
+
+ unless has_attribute?(:created_at)
+ raise ArgumentError, 'pipeline not fully loaded'
end
+
+ (Time.current - created_at).ceil / 60
end
private
@@ -1363,6 +1367,21 @@ module Ci
project.repository.keep_around(self.sha, self.before_sha)
end
+ def observe_age_in_minutes
+ return unless age_metric_enabled?
+ return unless persisted? && has_attribute?(:created_at)
+
+ ::Gitlab::Ci::Pipeline::Metrics
+ .pipeline_age_histogram
+ .observe({}, age_in_minutes)
+ end
+
+ def age_metric_enabled?
+ ::Gitlab::SafeRequestStore.fetch(:age_metric_enabled) do
+ ::Feature.enabled?(:ci_pipeline_age_histogram, type: :ops)
+ end
+ end
+
# Without using `unscoped`, caller scope is also included into the query.
# Using `unscoped` here will be redundant after Rails 6.1
def object_hierarchy(options = {})
diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb
index 2284a05bcc9..cdc3d69f754 100644
--- a/app/models/ci/pipeline_artifact.rb
+++ b/app/models/ci/pipeline_artifact.rb
@@ -51,6 +51,23 @@ module Ci
def find_by_file_type(file_type)
find_by(file_type: file_type)
end
+
+ def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:)
+ transaction do
+ pipeline.pipeline_artifacts.find_by_file_type(file_type)&.destroy!
+
+ pipeline.pipeline_artifacts.create!(
+ file_type: file_type,
+ project_id: pipeline.project_id,
+ size: size,
+ file: file,
+ file_format: REPORT_TYPES[file_type],
+ expire_at: EXPIRATION_DATE.from_now
+ )
+ end
+ rescue ActiveRecord::ActiveRecordError => err
+ Gitlab::ErrorTracking.track_and_raise_exception(err, { pipeline_id: pipeline.id, file_type: file_type })
+ end
end
def present
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 61194c9b7d1..f41ad890184 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -2,6 +2,7 @@
module Ci
class Runner < Ci::ApplicationRecord
+ prepend Ci::BulkInsertableTags
include Gitlab::SQL::Pattern
include RedisCacheable
include ChronicDurationAttribute
@@ -14,6 +15,8 @@ module Ci
include Presentable
include EachBatch
+ ignore_column :semver, remove_with: '15.3', remove_after: '2022-07-22'
+
add_authentication_token_field :token, encrypted: :optional, expires_at: :compute_token_expiration, expiration_enforced?: :token_expiration_enforced?
enum access_level: {
@@ -75,9 +78,9 @@ module Ci
has_many :groups, through: :runner_namespaces, disable_joins: true
has_one :last_build, -> { order('id DESC') }, class_name: 'Ci::Build'
+ has_one :runner_version, primary_key: :version, foreign_key: :version, class_name: 'Ci::RunnerVersion'
before_save :ensure_token
- before_save :update_semver, if: -> { version_changed? }
scope :active, -> (value = true) { where(active: value) }
scope :paused, -> { active(false) }
@@ -430,7 +433,6 @@ module Ci
values = values&.slice(:version, :revision, :platform, :architecture, :ip_address, :config, :executor) || {}
values[:contacted_at] = Time.current
values[:executor_type] = EXECUTOR_NAME_TO_TYPES.fetch(values.delete(:executor), :unknown)
- values[:semver] = semver_from_version(values[:version])
cache_attributes(values)
@@ -451,16 +453,6 @@ module Ci
read_attribute(:contacted_at)
end
- def semver_from_version(version)
- parsed_runner_version = ::Gitlab::VersionInfo.parse(version)
-
- parsed_runner_version.valid? ? parsed_runner_version.to_s : nil
- end
-
- def update_semver
- self.semver = semver_from_version(self.version)
- end
-
def namespace_ids
strong_memoize(:namespace_ids) do
runner_namespaces.pluck(:namespace_id).compact
@@ -484,6 +476,10 @@ module Ci
private
+ scope :with_upgrade_status, ->(upgrade_status) do
+ Ci::Runner.joins(:runner_version).where(runner_version: { status: upgrade_status })
+ end
+
EXECUTOR_NAME_TO_TYPES = {
'unknown' => :unknown,
'custom' => :custom,
diff --git a/app/models/ci/runner_version.rb b/app/models/ci/runner_version.rb
new file mode 100644
index 00000000000..6b2d0060c9b
--- /dev/null
+++ b/app/models/ci/runner_version.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Ci
+ class RunnerVersion < Ci::ApplicationRecord
+ include EachBatch
+ include EnumWithNil
+
+ enum_with_nil status: {
+ not_processed: nil,
+ invalid_version: -1,
+ unknown: 0,
+ not_available: 1,
+ available: 2,
+ recommended: 3
+ }
+
+ STATUS_DESCRIPTIONS = {
+ invalid_version: 'Runner version is not valid.',
+ unknown: 'Upgrade status is unknown.',
+ not_available: 'Upgrade is not available for the runner.',
+ available: 'Upgrade is available for the runner.',
+ recommended: 'Upgrade is available and recommended for the runner.'
+ }.freeze
+
+ # Override auto generated negative scope (from available) so the scope has expected behavior
+ scope :not_available, -> { where(status: :not_available) }
+
+ # 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
+ scope :potentially_outdated, -> { where(status: [nil, :not_available, :available, :unknown]) }
+
+ validates :version, length: { maximum: 2048 }
+ end
+end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 8c4e97ac840..f03d1e96a4b 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -142,7 +142,7 @@ module Ci
end
def latest_stage_status
- statuses.latest.composite_status(project: project) || 'skipped'
+ statuses.latest.composite_status || 'skipped'
end
end
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 5bf5ae51ec8..c4db4754c52 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -4,6 +4,9 @@ module Ci
class Trigger < Ci::ApplicationRecord
include Presentable
include Limitable
+ include IgnorableColumns
+
+ ignore_column :ref, remove_with: '15.4', remove_after: '2022-08-22'
self.limit_name = 'pipeline_triggers'
self.limit_scope = :project
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 1e91f248fc4..c80c2ebe69a 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -18,5 +18,9 @@ module Ci
scope :unprotected, -> { where(protected: false) }
scope :by_environment_scope, -> (environment_scope) { where(environment_scope: environment_scope) }
+
+ def audit_details
+ key
+ end
end
end