2020-03-13 15:44:24 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module Ci
|
2020-04-08 14:13:33 +05:30
|
|
|
class CreateJobArtifactsService < ::BaseService
|
2020-03-13 15:44:24 +05:30
|
|
|
ArtifactsExistError = Class.new(StandardError)
|
2020-04-08 14:13:33 +05:30
|
|
|
OBJECT_STORAGE_ERRORS = [
|
|
|
|
Errno::EIO,
|
|
|
|
Google::Apis::ServerError,
|
|
|
|
Signet::RemoteServerError
|
|
|
|
].freeze
|
2020-03-13 15:44:24 +05:30
|
|
|
|
|
|
|
def execute(job, artifacts_file, params, metadata_file: nil)
|
2020-04-08 14:13:33 +05:30
|
|
|
return success if sha256_matches_existing_artifact?(job, params['artifact_type'], artifacts_file)
|
|
|
|
|
|
|
|
artifact, artifact_metadata = build_artifact(job, artifacts_file, params, metadata_file)
|
|
|
|
result = parse_artifact(job, artifact)
|
|
|
|
|
|
|
|
return result unless result[:status] == :success
|
|
|
|
|
|
|
|
persist_artifact(job, artifact, artifact_metadata)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def build_artifact(job, artifacts_file, params, metadata_file)
|
2020-03-13 15:44:24 +05:30
|
|
|
expire_in = params['expire_in'] ||
|
|
|
|
Gitlab::CurrentSettings.current_application_settings.default_artifacts_expire_in
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
artifact = Ci::JobArtifact.new(
|
|
|
|
job_id: job.id,
|
2020-03-13 15:44:24 +05:30
|
|
|
project: job.project,
|
|
|
|
file: artifacts_file,
|
|
|
|
file_type: params['artifact_type'],
|
|
|
|
file_format: params['artifact_format'],
|
|
|
|
file_sha256: artifacts_file.sha256,
|
|
|
|
expire_in: expire_in)
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
artifact_metadata = if metadata_file
|
|
|
|
Ci::JobArtifact.new(
|
|
|
|
job_id: job.id,
|
|
|
|
project: job.project,
|
|
|
|
file: metadata_file,
|
|
|
|
file_type: :metadata,
|
|
|
|
file_format: :gzip,
|
|
|
|
file_sha256: metadata_file.sha256,
|
|
|
|
expire_in: expire_in)
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
if Feature.enabled?(:keep_latest_artifact_for_ref, job.project)
|
|
|
|
artifact.locked = true
|
|
|
|
artifact_metadata&.locked = true
|
|
|
|
end
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
[artifact, artifact_metadata]
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
def parse_artifact(job, artifact)
|
|
|
|
unless Feature.enabled?(:ci_synchronous_artifact_parsing, job.project, default_enabled: true)
|
|
|
|
return success
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
case artifact.file_type
|
|
|
|
when 'dotenv' then parse_dotenv_artifact(job, artifact)
|
2020-05-24 23:13:21 +05:30
|
|
|
when 'cluster_applications' then parse_cluster_applications_artifact(job, artifact)
|
2020-04-08 14:13:33 +05:30
|
|
|
else success
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
end
|
|
|
|
|
2020-04-08 14:13:33 +05:30
|
|
|
def persist_artifact(job, artifact, artifact_metadata)
|
|
|
|
Ci::JobArtifact.transaction do
|
|
|
|
artifact.save!
|
|
|
|
artifact_metadata&.save!
|
2020-05-24 23:13:21 +05:30
|
|
|
unlock_previous_artifacts!(artifact)
|
2020-04-08 14:13:33 +05:30
|
|
|
|
|
|
|
# NOTE: The `artifacts_expire_at` column is already deprecated and to be removed in the near future.
|
|
|
|
job.update_column(:artifacts_expire_at, artifact.expire_at)
|
|
|
|
end
|
|
|
|
|
|
|
|
success
|
|
|
|
rescue ActiveRecord::RecordNotUnique => error
|
|
|
|
track_exception(error, job, params)
|
|
|
|
error('another artifact of the same type already exists', :bad_request)
|
|
|
|
rescue *OBJECT_STORAGE_ERRORS => error
|
|
|
|
track_exception(error, job, params)
|
|
|
|
error(error.message, :service_unavailable)
|
|
|
|
rescue => error
|
2020-04-22 19:07:51 +05:30
|
|
|
track_exception(error, job, params)
|
2020-04-08 14:13:33 +05:30
|
|
|
error(error.message, :bad_request)
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
|
2020-05-24 23:13:21 +05:30
|
|
|
def unlock_previous_artifacts!(artifact)
|
|
|
|
return unless Feature.enabled?(:keep_latest_artifact_for_ref, artifact.job.project)
|
|
|
|
|
|
|
|
Ci::JobArtifact.for_ref(artifact.job.ref, artifact.project_id).locked.update_all(locked: false)
|
|
|
|
end
|
|
|
|
|
2020-03-13 15:44:24 +05:30
|
|
|
def sha256_matches_existing_artifact?(job, artifact_type, artifacts_file)
|
|
|
|
existing_artifact = job.job_artifacts.find_by_file_type(artifact_type)
|
|
|
|
return false unless existing_artifact
|
|
|
|
|
|
|
|
existing_artifact.file_sha256 == artifacts_file.sha256
|
|
|
|
end
|
2020-04-08 14:13:33 +05:30
|
|
|
|
|
|
|
def track_exception(error, job, params)
|
|
|
|
Gitlab::ErrorTracking.track_exception(error,
|
|
|
|
job_id: job.id,
|
|
|
|
project_id: job.project_id,
|
|
|
|
uploading_type: params['artifact_type']
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def parse_dotenv_artifact(job, artifact)
|
|
|
|
Ci::ParseDotenvArtifactService.new(job.project, current_user).execute(artifact)
|
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
def parse_cluster_applications_artifact(job, artifact)
|
|
|
|
Clusters::ParseClusterApplicationsArtifactService.new(job, job.user).execute(artifact)
|
|
|
|
end
|
2020-03-13 15:44:24 +05:30
|
|
|
end
|
|
|
|
end
|