2018-12-05 23:21:45 +05:30
# frozen_string_literal: true
2017-09-10 17:25:29 +05:30
class Projects :: JobsController < Projects :: ApplicationController
2018-05-09 12:01:36 +05:30
include SendFileUpload
2018-12-13 13:39:08 +05:30
include ContinueParams
2017-09-10 17:25:29 +05:30
2022-01-26 12:08:38 +05:30
before_action :find_job_as_build , except : [ :index , :play , :show ]
before_action :find_job_as_processable , only : [ :play , :show ]
2021-02-22 17:27:13 +05:30
before_action :authorize_read_build_trace! , only : [ :trace , :raw ]
2018-11-08 19:23:39 +05:30
before_action :authorize_read_build!
2017-09-10 17:25:29 +05:30
before_action :authorize_update_build! ,
2021-10-27 15:23:28 +05:30
except : [ :index , :show , :status , :raw , :trace , :erase , :cancel , :unschedule ]
2018-03-17 18:26:18 +05:30
before_action :authorize_erase_build! , only : [ :erase ]
2019-02-15 15:39:39 +05:30
before_action :authorize_use_build_terminal! , only : [ :terminal , :terminal_websocket_authorize ]
2018-11-08 19:23:39 +05:30
before_action :verify_api_request! , only : :terminal_websocket_authorize
2020-06-23 00:09:42 +05:30
before_action :authorize_create_proxy_build! , only : :proxy_websocket_authorize
before_action :verify_proxy_request! , only : :proxy_websocket_authorize
2021-04-29 21:17:54 +05:30
before_action :push_jobs_table_vue , only : [ :index ]
2017-09-10 17:25:29 +05:30
2021-09-30 23:02:18 +05:30
before_action do
push_frontend_feature_flag ( :infinitely_collapsible_sections , @project , default_enabled : :yaml )
end
2017-09-10 17:25:29 +05:30
layout 'project'
2021-01-03 14:25:43 +05:30
feature_category :continuous_integration
2017-09-10 17:25:29 +05:30
def index
2020-01-01 13:55:28 +05:30
# We need all builds for tabs counters
2020-04-08 14:13:33 +05:30
@all_builds = Ci :: JobsFinder . new ( current_user : current_user , project : @project ) . execute
2020-01-01 13:55:28 +05:30
2017-09-10 17:25:29 +05:30
@scope = params [ :scope ]
2020-04-08 14:13:33 +05:30
@builds = Ci :: JobsFinder . new ( current_user : current_user , project : @project , params : params ) . execute
2020-01-01 13:55:28 +05:30
@builds = @builds . eager_load_everything
2018-03-17 18:26:18 +05:30
@builds = @builds . page ( params [ :page ] ) . per ( 30 ) . without_count
2017-09-10 17:25:29 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ActiveRecord
2017-09-10 17:25:29 +05:30
def show
respond_to do | format |
format . html
format . json do
Gitlab :: PollingInterval . set_header ( response , interval : 10_000 )
2022-01-26 12:08:38 +05:30
render json : Ci :: JobSerializer
2017-09-10 17:25:29 +05:30
. new ( project : @project , current_user : @current_user )
2021-11-18 22:05:49 +05:30
. represent ( @build . present ( current_user : current_user ) , { } , BuildDetailsEntity )
2017-09-10 17:25:29 +05:30
end
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ActiveRecord
2017-09-10 17:25:29 +05:30
def trace
2021-02-22 17:27:13 +05:30
@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-09-10 17:25:29 +05:30
end
end
2021-02-22 17:27:13 +05:30
else
head :no_content
2017-09-10 17:25:29 +05:30
end
end
def retry
return respond_422 unless @build . retryable?
build = Ci :: Build . retry ( @build , current_user )
redirect_to build_path ( build )
end
def play
return respond_422 unless @build . playable?
2021-01-03 14:25:43 +05:30
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
2017-09-10 17:25:29 +05:30
end
def cancel
2021-10-27 15:23:28 +05:30
service_response = Ci :: BuildCancelService . new ( @build , current_user ) . execute
2017-09-10 17:25:29 +05:30
2021-10-27 15:23:28 +05:30
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-12-13 13:39:08 +05:30
else
2021-10-27 15:23:28 +05:30
head service_response . http_status
2018-12-13 13:39:08 +05:30
end
2017-09-10 17:25:29 +05:30
end
2018-12-05 23:21:45 +05:30
def unschedule
2021-10-27 15:23:28 +05:30
service_response = Ci :: BuildUnscheduleService . new ( @build , current_user ) . execute
2018-12-05 23:21:45 +05:30
2021-10-27 15:23:28 +05:30
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-12-05 23:21:45 +05:30
end
2017-09-10 17:25:29 +05:30
def status
2022-01-26 12:08:38 +05:30
render json : Ci :: JobSerializer
2017-09-10 17:25:29 +05:30
. new ( project : @project , current_user : @current_user )
2021-11-18 22:05:49 +05:30
. represent_status ( @build . present ( current_user : current_user ) )
2017-09-10 17:25:29 +05:30
end
def erase
if @build . erase ( erased_by : current_user )
redirect_to project_job_path ( project , @build ) ,
2019-07-07 11:18:12 +05:30
notice : _ ( " Job has been successfully erased! " )
2017-09-10 17:25:29 +05:30
else
respond_422
end
end
def raw
2021-12-11 22:18:48 +05:30
if @build . trace . archived_trace_exist?
2019-02-15 15:39:39 +05:30
workhorse_set_content_type!
2021-12-11 22:18:48 +05:30
send_upload ( @build . job_artifacts_trace . file ,
2018-05-09 12:01:36 +05:30
send_params : raw_send_params ,
redirect_params : raw_redirect_params )
else
2021-01-03 14:25:43 +05:30
@build . trace . read do | stream |
2018-05-09 12:01:36 +05:30
if stream . file?
2019-02-15 15:39:39 +05:30
workhorse_set_content_type!
2018-05-09 12:01:36 +05:30
send_file stream . path , type : 'text/plain; charset=utf-8' , disposition : 'inline'
else
2019-02-15 15:39:39 +05:30
# 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-05-09 12:01:36 +05:30
end
2017-09-10 17:25:29 +05:30
end
end
end
2018-11-08 19:23:39 +05:30
def terminal
end
# GET .../terminal.ws : implemented in gitlab-workhorse
def terminal_websocket_authorize
set_workhorse_internal_api_content_type
2019-07-07 11:18:12 +05:30
render json : Gitlab :: Workhorse . channel_websocket ( @build . terminal_specification )
2018-11-08 19:23:39 +05:30
end
2020-06-23 00:09:42 +05:30
def proxy_websocket_authorize
render json : proxy_websocket_service ( build_service_specification )
end
2017-09-10 17:25:29 +05:30
private
2021-02-22 17:27:13 +05:30
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-09-10 17:25:29 +05:30
def authorize_update_build!
2021-01-03 14:25:43 +05:30
return access_denied! unless can? ( current_user , :update_build , @build )
2017-09-10 17:25:29 +05:30
end
2018-03-17 18:26:18 +05:30
def authorize_erase_build!
2021-01-03 14:25:43 +05:30
return access_denied! unless can? ( current_user , :erase_build , @build )
2018-03-17 18:26:18 +05:30
end
2018-11-08 19:23:39 +05:30
def authorize_use_build_terminal!
2021-01-03 14:25:43 +05:30
return access_denied! unless can? ( current_user , :create_build_terminal , @build )
2018-11-08 19:23:39 +05:30
end
2020-06-23 00:09:42 +05:30
def authorize_create_proxy_build!
2021-01-03 14:25:43 +05:30
return access_denied! unless can? ( current_user , :create_build_service_proxy , @build )
2020-06-23 00:09:42 +05:30
end
2018-11-08 19:23:39 +05:30
def verify_api_request!
Gitlab :: Workhorse . verify_api_request! ( request . headers )
end
2020-06-23 00:09:42 +05:30
def verify_proxy_request!
verify_api_request!
set_workhorse_internal_api_content_type
end
2018-05-09 12:01:36 +05:30
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-10-12 21:52:04 +05:30
def play_params
params . permit ( job_variables_attributes : % i [ key secret_value ] )
end
2021-01-03 14:25:43 +05:30
def find_job_as_build
@build = project . builds . find ( params [ :id ] )
2017-09-10 17:25:29 +05:30
end
2021-01-03 14:25:43 +05:30
def find_job_as_processable
2021-02-22 17:27:13 +05:30
@build = project . processables . find ( params [ :id ] )
2021-01-03 14:25:43 +05:30
end
2017-09-10 17:25:29 +05:30
def build_path ( build )
project_job_path ( build . project , build )
end
2019-02-15 15:39:39 +05:30
def raw_trace_content_disposition ( raw_data )
2021-04-01 16:36:13 +05:30
mime_type = Gitlab :: Utils :: MimeType . from_string ( raw_data )
2019-02-15 15:39:39 +05:30
# if mime_type is nil can also represent 'text/plain'
2021-04-01 16:36:13 +05:30
return 'inline' if mime_type . nil? || mime_type == 'text/plain'
2019-02-15 15:39:39 +05:30
'attachment'
end
2019-12-04 20:38:33 +05:30
2020-06-23 00:09:42 +05:30
def build_service_specification
2021-01-03 14:25:43 +05:30
@build . service_specification ( service : params [ 'service' ] ,
port : params [ 'port' ] ,
path : params [ 'path' ] ,
subprotocols : proxy_subprotocol )
2020-06-23 00:09:42 +05:30
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-29 21:17:54 +05:30
def push_jobs_table_vue
push_frontend_feature_flag ( :jobs_table_vue , @project , default_enabled : :yaml )
end
2020-06-23 00:09:42 +05:30
end