2018-09-25 23:45:43 -04:00
# frozen_string_literal: true
2017-05-16 07:41:15 -04:00
class Projects :: JobsController < Projects :: ApplicationController
2018-03-02 16:19:17 -05:00
include SendFileUpload
2018-08-26 11:57:51 -04:00
include ContinueParams
2018-03-02 16:19:17 -05:00
2021-12-10 13:14:42 -05:00
before_action :find_job_as_build , except : [ :index , :play , :show ]
before_action :find_job_as_processable , only : [ :play , :show ]
2020-12-10 10:10:12 -05:00
before_action :authorize_read_build_trace! , only : [ :trace , :raw ]
2018-07-05 09:55:10 -04:00
before_action :authorize_read_build!
2017-05-05 07:24:07 -04:00
before_action :authorize_update_build! ,
2021-07-26 08:10:08 -04:00
except : [ :index , :show , :status , :raw , :trace , :erase , :cancel , :unschedule ]
2017-11-06 08:20:44 -05:00
before_action :authorize_erase_build! , only : [ :erase ]
2018-12-17 13:22:03 -05:00
before_action :authorize_use_build_terminal! , only : [ :terminal , :terminal_websocket_authorize ]
2018-07-05 09:55:10 -04:00
before_action :verify_api_request! , only : :terminal_websocket_authorize
2020-05-25 11:07:58 -04:00
before_action :authorize_create_proxy_build! , only : :proxy_websocket_authorize
before_action :verify_proxy_request! , only : :proxy_websocket_authorize
2021-04-19 14:09:09 -04:00
before_action :push_jobs_table_vue , only : [ :index ]
2017-05-05 07:24:07 -04:00
2021-07-12 14:09:09 -04:00
before_action do
push_frontend_feature_flag ( :infinitely_collapsible_sections , @project , default_enabled : :yaml )
2022-01-12 10:13:54 -05:00
push_frontend_feature_flag ( :trigger_job_retry_action , @project , default_enabled : :yaml )
2021-07-12 14:09:09 -04:00
end
2016-02-08 02:51:10 -05:00
layout 'project'
2015-10-07 09:24:32 -04:00
2020-10-08 14:08:32 -04:00
feature_category :continuous_integration
2015-10-14 06:15:03 -04:00
def index
2019-11-25 19:06:28 -05:00
# We need all builds for tabs counters
2020-03-13 20:09:30 -04:00
@all_builds = Ci :: JobsFinder . new ( current_user : current_user , project : @project ) . execute
2019-11-25 19:06:28 -05:00
2015-10-14 06:15:03 -04:00
@scope = params [ :scope ]
2020-03-13 20:09:30 -04:00
@builds = Ci :: JobsFinder . new ( current_user : current_user , project : @project , params : params ) . execute
2019-11-25 19:06:28 -05:00
@builds = @builds . eager_load_everything
2018-01-11 08:22:52 -05:00
@builds = @builds . page ( params [ :page ] ) . per ( 30 ) . without_count
2015-10-14 06:15:03 -04:00
end
2018-08-27 11:31:01 -04:00
# rubocop: disable CodeReuse/ActiveRecord
2015-10-06 11:11:10 -04:00
def show
2017-05-23 11:10:07 -04:00
respond_to do | format |
format . html
format . json do
Gitlab :: PollingInterval . set_header ( response , interval : 10_000 )
2021-12-13 16:14:32 -05:00
render json : Ci :: JobSerializer
2017-05-23 11:10:07 -04:00
. new ( project : @project , current_user : @current_user )
2021-09-22 14:11:13 -04:00
. represent ( @build . present ( current_user : current_user ) , { } , BuildDetailsEntity )
2017-05-23 11:10:07 -04:00
end
end
2015-10-06 11:11:10 -04:00
end
2018-08-27 11:31:01 -04:00
# rubocop: enable CodeReuse/ActiveRecord
2015-10-06 11:11:10 -04:00
2016-05-09 12:59:45 -04:00
def trace
2021-01-05 22:10:22 -05:00
@build . trace . being_watched! if @build . running?
if @build . has_trace?
@build . trace . read do | stream |
respond_to do | format |
format . json do
build_trace = Ci :: BuildTrace . new (
build : @build ,
stream : stream ,
state : params [ :state ] )
render json : BuildTraceSerializer
. new ( project : @project , current_user : @current_user )
. represent ( build_trace )
end
2017-04-06 12:20:27 -04:00
end
2016-05-09 12:59:45 -04:00
end
2021-01-05 22:10:22 -05:00
else
head :no_content
2016-05-09 12:59:45 -04:00
end
end
2015-10-07 09:24:32 -04:00
def retry
2017-04-28 05:38:32 -04:00
return respond_422 unless @build . retryable?
2015-10-07 09:24:32 -04:00
2016-06-10 17:36:54 -04:00
build = Ci :: Build . retry ( @build , current_user )
2015-11-03 05:44:07 -05:00
redirect_to build_path ( build )
2015-10-07 09:24:32 -04:00
end
2016-07-16 12:39:58 -04:00
def play
2017-04-28 05:38:32 -04:00
return respond_422 unless @build . playable?
2016-07-16 12:39:58 -04:00
2020-10-13 05:08:27 -04:00
job = @build . play ( current_user , play_params [ :job_variables_attributes ] )
if job . is_a? ( Ci :: Bridge )
redirect_to pipeline_path ( job . pipeline )
else
redirect_to build_path ( job )
end
2016-07-16 12:39:58 -04:00
end
2015-10-07 09:24:32 -04:00
def cancel
2021-07-26 08:10:08 -04:00
service_response = Ci :: BuildCancelService . new ( @build , current_user ) . execute
2017-04-28 05:38:32 -04:00
2021-07-26 08:10:08 -04:00
if service_response . success?
destination = continue_params [ :to ] . presence || builds_project_pipeline_path ( @project , @build . pipeline . id )
redirect_to destination
elsif service_response . http_status == :forbidden
access_denied!
2018-08-26 11:57:51 -04:00
else
2021-07-26 08:10:08 -04:00
head service_response . http_status
2018-08-26 11:57:51 -04:00
end
2015-10-07 09:24:32 -04:00
end
2018-09-18 03:12:13 -04:00
def unschedule
2021-07-26 08:10:08 -04:00
service_response = Ci :: BuildUnscheduleService . new ( @build , current_user ) . execute
2018-09-18 03:12:13 -04:00
2021-07-26 08:10:08 -04:00
if service_response . success?
redirect_to build_path ( @build )
elsif service_response . http_status == :forbidden
access_denied!
else
head service_response . http_status
end
2018-09-18 03:12:13 -04:00
end
2016-02-08 07:53:30 -05:00
def status
2021-12-13 16:14:32 -05:00
render json : Ci :: JobSerializer
2017-05-09 00:15:34 -04:00
. new ( project : @project , current_user : @current_user )
2021-09-22 14:11:13 -04:00
. represent_status ( @build . present ( current_user : current_user ) )
2016-02-08 07:53:30 -05:00
end
2016-02-08 02:57:09 -05:00
2016-02-01 05:59:05 -05:00
def erase
2017-04-28 05:38:32 -04:00
if @build . erase ( erased_by : current_user )
2017-06-29 13:06:35 -04:00
redirect_to project_job_path ( project , @build ) ,
2019-03-27 12:52:52 -04:00
notice : _ ( " Job has been successfully erased! " )
2017-04-28 05:38:32 -04:00
else
respond_422
end
2016-01-22 09:29:02 -05:00
end
2016-04-16 17:32:18 -04:00
def raw
2021-10-29 08:14:45 -04:00
if @build . trace . archived_trace_exist?
2018-12-06 16:22:39 -05:00
workhorse_set_content_type!
2021-10-29 08:14:45 -04:00
send_upload ( @build . job_artifacts_trace . file ,
2018-04-03 08:30:14 -04:00
send_params : raw_send_params ,
redirect_params : raw_redirect_params )
else
2020-10-13 05:08:27 -04:00
@build . trace . read do | stream |
2018-04-03 08:30:14 -04:00
if stream . file?
2018-12-06 16:22:39 -05:00
workhorse_set_content_type!
2018-04-03 08:30:14 -04:00
send_file stream . path , type : 'text/plain; charset=utf-8' , disposition : 'inline'
else
2018-12-06 16:22:39 -05:00
# In this case we can't use workhorse_set_content_type! and let
# Workhorse handle the response because the data is streamed directly
# to the user but, because we have the trace content, we can calculate
# the proper content type and disposition here.
raw_data = stream . raw
send_data raw_data , type : 'text/plain; charset=utf-8' , disposition : raw_trace_content_disposition ( raw_data ) , filename : 'job.log'
2018-04-03 08:30:14 -04:00
end
2017-04-06 12:20:27 -04:00
end
2016-04-16 17:32:18 -04:00
end
end
2018-07-05 09:55:10 -04:00
def terminal
end
# GET .../terminal.ws : implemented in gitlab-workhorse
def terminal_websocket_authorize
set_workhorse_internal_api_content_type
2019-04-04 14:32:02 -04:00
render json : Gitlab :: Workhorse . channel_websocket ( @build . terminal_specification )
2018-07-05 09:55:10 -04:00
end
2020-05-25 11:07:58 -04:00
def proxy_websocket_authorize
render json : proxy_websocket_service ( build_service_specification )
end
2015-10-06 11:11:10 -04:00
private
2020-12-10 10:10:12 -05:00
def authorize_read_build_trace!
return if can? ( current_user , :read_build_trace , @build )
msg = _ (
" You must have developer or higher permissions in the associated project to view job logs when debug trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline configuration or CI/CD settings. " \
" If you need to view this job log, a project maintainer must add you to the project with developer permissions or higher. "
)
return access_denied! ( msg ) if @build . debug_mode?
access_denied! ( _ ( 'The current user is not authorized to access the job log.' ) )
end
2017-05-05 07:24:07 -04:00
def authorize_update_build!
2020-10-13 05:08:27 -04:00
return access_denied! unless can? ( current_user , :update_build , @build )
2017-05-05 07:24:07 -04:00
end
2017-11-06 08:20:44 -05:00
def authorize_erase_build!
2020-10-13 05:08:27 -04:00
return access_denied! unless can? ( current_user , :erase_build , @build )
2017-11-06 08:20:44 -05:00
end
2018-07-05 09:55:10 -04:00
def authorize_use_build_terminal!
2020-10-13 05:08:27 -04:00
return access_denied! unless can? ( current_user , :create_build_terminal , @build )
2018-07-05 09:55:10 -04:00
end
2020-05-25 11:07:58 -04:00
def authorize_create_proxy_build!
2020-10-13 05:08:27 -04:00
return access_denied! unless can? ( current_user , :create_build_service_proxy , @build )
2020-05-25 11:07:58 -04:00
end
2018-07-05 09:55:10 -04:00
def verify_api_request!
Gitlab :: Workhorse . verify_api_request! ( request . headers )
end
2020-05-25 11:07:58 -04:00
def verify_proxy_request!
verify_api_request!
set_workhorse_internal_api_content_type
end
2018-04-03 08:30:14 -04:00
def raw_send_params
{ type : 'text/plain; charset=utf-8' , disposition : 'inline' }
end
def raw_redirect_params
{ query : { 'response-content-type' = > 'text/plain; charset=utf-8' , 'response-content-disposition' = > 'inline' } }
end
2019-07-29 03:43:10 -04:00
def play_params
params . permit ( job_variables_attributes : % i [ key secret_value ] )
end
2020-10-13 05:08:27 -04:00
def find_job_as_build
@build = project . builds . find ( params [ :id ] )
2015-10-06 11:11:10 -04:00
end
2015-10-07 09:24:32 -04:00
2020-10-13 05:08:27 -04:00
def find_job_as_processable
2020-12-02 22:09:27 -05:00
@build = project . processables . find ( params [ :id ] )
2020-10-13 05:08:27 -04:00
end
2015-10-07 09:24:32 -04:00
def build_path ( build )
2017-06-29 13:06:35 -04:00
project_job_path ( build . project , build )
2015-10-07 09:24:32 -04:00
end
2018-12-06 16:22:39 -05:00
def raw_trace_content_disposition ( raw_data )
2021-03-25 02:09:02 -04:00
mime_type = Gitlab :: Utils :: MimeType . from_string ( raw_data )
2018-12-06 16:22:39 -05:00
# if mime_type is nil can also represent 'text/plain'
2021-03-25 02:09:02 -04:00
return 'inline' if mime_type . nil? || mime_type == 'text/plain'
2018-12-06 16:22:39 -05:00
'attachment'
end
2019-09-13 09:26:31 -04:00
2020-05-25 11:07:58 -04:00
def build_service_specification
2020-10-13 05:08:27 -04:00
@build . service_specification ( service : params [ 'service' ] ,
port : params [ 'port' ] ,
path : params [ 'path' ] ,
subprotocols : proxy_subprotocol )
2020-05-25 11:07:58 -04:00
end
def proxy_subprotocol
# This will allow to reuse the same subprotocol set
# in the original websocket connection
request . headers [ 'HTTP_SEC_WEBSOCKET_PROTOCOL' ] . presence || :: Ci :: BuildRunnerSession :: TERMINAL_SUBPROTOCOL
end
# This method provides the information to Workhorse
# about the service we want to proxy to.
# For security reasons, in case this operation is started by JS,
# it's important to use only sourced GitLab JS code
def proxy_websocket_service ( service )
service [ :url ] = :: Gitlab :: UrlHelpers . as_wss ( service [ :url ] )
:: Gitlab :: Workhorse . channel_websocket ( service )
end
2021-04-19 14:09:09 -04:00
def push_jobs_table_vue
push_frontend_feature_flag ( :jobs_table_vue , @project , default_enabled : :yaml )
end
2020-05-25 11:07:58 -04:00
end