Refactor submission scoring and testing

* Properly cancel code execution if running is prohibited
* Correctly monitor duration with Sentry
* Align methods with running submissions
This commit is contained in:
Sebastian Serth
2023-02-16 23:59:07 +01:00
committed by Sebastian Serth
parent a1ec4579fe
commit 9c3ec3c7ff
2 changed files with 45 additions and 37 deletions

View File

@ -239,31 +239,32 @@ class SubmissionsController < ApplicationController
# rubocop:enable Metrics/CyclomaticComplexity: # rubocop:enable Metrics/CyclomaticComplexity:
def score def score
hijack do |tubesock| client_socket = nil
tubesock.onopen do |_event| disable_scoring = @embed_options[:disable_score] || !@submission.exercise.teacher_defined_assessment?
switch_locale do
return kill_client_socket(tubesock) if @embed_options[:disable_score] || !@submission.exercise.teacher_defined_assessment?
# The score is stored separately, we can forward it to the client immediately hijack do |tubesock|
tubesock.send_data(JSON.dump(@submission.calculate_score)) client_socket = tubesock
# To enable hints when scoring a submission, uncomment the next line: tubesock.onopen do |_event|
# send_hints(tubesock, StructuredError.where(submission: @submission)) kill_client_socket(tubesock) and return true if disable_scoring
kill_client_socket(tubesock)
rescue Runner::Error => e
extract_durations(e)
send_and_store tubesock, {cmd: :status, status: :container_depleted}
kill_client_socket(tubesock)
Rails.logger.debug { "Runner error while scoring submission #{@submission.id}: #{e.message}" }
@testrun[:passed] = false
save_testrun_output 'assess'
rescue StandardError => e
Sentry.capture_exception(e)
raise e
ensure
ActiveRecord::Base.connection_pool.release_connection
end
end end
end end
# If scoring is not allowed (and the socket is closed), we can stop here.
return true if disable_scoring
# The score is stored separately, we can forward it to the client immediately
client_socket&.send_data(JSON.dump(@submission.calculate_score))
# To enable hints when scoring a submission, uncomment the next line:
# send_hints(client_socket, StructuredError.where(submission: @submission))
kill_client_socket(client_socket)
rescue Runner::Error => e
extract_durations(e)
send_and_store client_socket, {cmd: :status, status: :container_depleted}
kill_client_socket(client_socket)
Rails.logger.debug { "Runner error while scoring submission #{@submission.id}: #{e.message}" }
@testrun[:passed] = false
ensure
save_testrun_output 'assess'
end end
def create def create
@ -275,24 +276,29 @@ class SubmissionsController < ApplicationController
def statistics; end def statistics; end
def test def test
hijack do |tubesock| client_socket = nil
tubesock.onopen do |_event|
switch_locale do
return kill_client_socket(tubesock) if @embed_options[:disable_run]
# The score is stored separately, we can forward it to the client immediately hijack do |tubesock|
tubesock.send_data(JSON.dump(@submission.test(@file))) client_socket = tubesock
kill_client_socket(tubesock) tubesock.onopen do |_event|
rescue Runner::Error => e kill_client_socket(tubesock) and return true if @embed_options[:disable_run]
extract_durations(e)
send_and_store tubesock, {cmd: :status, status: :container_depleted}
kill_client_socket(tubesock)
Rails.logger.debug { "Runner error while testing submission #{@submission.id}: #{e.message}" }
@testrun[:passed] = false
save_testrun_output 'assess'
end
end end
end end
# If running is not allowed (and the socket is closed), we can stop here.
return true if @embed_options[:disable_run]
# The score is stored separately, we can forward it to the client immediately
client_socket&.send_data(JSON.dump(@submission.test(@file)))
kill_client_socket(client_socket)
rescue Runner::Error => e
extract_durations(e)
send_and_store client_socket, {cmd: :status, status: :container_depleted}
kill_client_socket(client_socket)
Rails.logger.debug { "Runner error while testing submission #{@submission.id}: #{e.message}" }
@testrun[:passed] = false
ensure
save_testrun_output 'assess'
end end
private private

View File

@ -233,6 +233,7 @@ describe SubmissionsController do
before do before do
allow_any_instance_of(described_class).to receive(:hijack) allow_any_instance_of(described_class).to receive(:hijack)
allow_any_instance_of(described_class).to receive(:kill_client_socket)
perform_request.call perform_request.call
end end
@ -247,6 +248,7 @@ describe SubmissionsController do
before do before do
file.update(hidden: false) file.update(hidden: false)
allow_any_instance_of(described_class).to receive(:hijack) allow_any_instance_of(described_class).to receive(:hijack)
allow_any_instance_of(described_class).to receive(:kill_client_socket)
get :test, params: {filename: "#{file.filepath}.json", id: submission.id} get :test, params: {filename: "#{file.filepath}.json", id: submission.id}
end end