@ -13,6 +13,10 @@ class SubmissionsController < ApplicationController
|
|||||||
before_action :set_mime_type, only: [:download_file, :render_file]
|
before_action :set_mime_type, only: [:download_file, :render_file]
|
||||||
skip_before_action :verify_authenticity_token, only: [:download_file, :render_file]
|
skip_before_action :verify_authenticity_token, only: [:download_file, :render_file]
|
||||||
|
|
||||||
|
def max_message_buffer_size
|
||||||
|
500
|
||||||
|
end
|
||||||
|
|
||||||
def authorize!
|
def authorize!
|
||||||
authorize(@submission || @submissions)
|
authorize(@submission || @submissions)
|
||||||
end
|
end
|
||||||
@ -139,7 +143,7 @@ class SubmissionsController < ApplicationController
|
|||||||
tubesock.onmessage do |data|
|
tubesock.onmessage do |data|
|
||||||
Rails.logger.info(Time.now.getutc.to_s + ": Client sending: " + data)
|
Rails.logger.info(Time.now.getutc.to_s + ": Client sending: " + data)
|
||||||
# Check whether the client send a JSON command and kill container
|
# Check whether the client send a JSON command and kill container
|
||||||
# if the command is 'client_exit', send it to docker otherwise.
|
# if the command is 'client_kill', send it to docker otherwise.
|
||||||
begin
|
begin
|
||||||
parsed = JSON.parse(data)
|
parsed = JSON.parse(data)
|
||||||
if parsed['cmd'] == 'client_kill'
|
if parsed['cmd'] == 'client_kill'
|
||||||
@ -166,21 +170,31 @@ class SubmissionsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def kill_socket(tubesock)
|
def kill_socket(tubesock)
|
||||||
|
# save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb)
|
||||||
|
save_run_output
|
||||||
|
|
||||||
# Hijacked connection needs to be notified correctly
|
# Hijacked connection needs to be notified correctly
|
||||||
tubesock.send_data JSON.dump({'cmd' => 'exit'})
|
tubesock.send_data JSON.dump({'cmd' => 'exit'})
|
||||||
tubesock.close
|
tubesock.close
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_message(message, tubesock, container)
|
def handle_message(message, tubesock, container)
|
||||||
|
@message_buffer ||= ""
|
||||||
# Handle special commands first
|
# Handle special commands first
|
||||||
if (/^#exit/.match(message))
|
if (/^#exit/.match(message))
|
||||||
kill_socket(tubesock)
|
# Just call exit_container on the docker_client.
|
||||||
|
# Do not call kill_socket for the websocket to the client here.
|
||||||
|
# @docker_client.exit_container closes the socket to the container,
|
||||||
|
# kill_socket is called in the "on close handler" of the websocket to the container
|
||||||
@docker_client.exit_container(container)
|
@docker_client.exit_container(container)
|
||||||
|
elsif /^#timeout/.match(message)
|
||||||
|
@message_buffer = 'timeout: ' + @message_buffer # add information that this run timed out to the buffer
|
||||||
else
|
else
|
||||||
# Filter out information about run_command, test_command, user or working directory
|
# Filter out information about run_command, test_command, user or working directory
|
||||||
run_command = @submission.execution_environment.run_command % command_substitutions(params[:filename])
|
run_command = @submission.execution_environment.run_command % command_substitutions(params[:filename])
|
||||||
test_command = @submission.execution_environment.test_command % command_substitutions(params[:filename])
|
test_command = @submission.execution_environment.test_command % command_substitutions(params[:filename])
|
||||||
if !(/root|workspace|#{run_command}|#{test_command}/.match(message))
|
if !(/root|workspace|#{run_command}|#{test_command}/.match(message))
|
||||||
|
@message_buffer += message if @message_buffer.size <= max_message_buffer_size
|
||||||
parse_message(message, 'stdout', tubesock)
|
parse_message(message, 'stdout', tubesock)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -228,6 +242,13 @@ class SubmissionsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def save_run_output
|
||||||
|
if !@message_buffer.blank?
|
||||||
|
@message_buffer = @message_buffer[(0..max_message_buffer_size-1)] # trim the string to max_message_buffer_size chars
|
||||||
|
Testrun.create(file: @file, submission: @submission, output: @message_buffer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def score
|
def score
|
||||||
hijack do |tubesock|
|
hijack do |tubesock|
|
||||||
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
|
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
|
||||||
|
@ -255,6 +255,12 @@ class DockerClient
|
|||||||
if(@tubesock)
|
if(@tubesock)
|
||||||
@tubesock.send_data JSON.dump({'cmd' => 'timeout'})
|
@tubesock.send_data JSON.dump({'cmd' => 'timeout'})
|
||||||
end
|
end
|
||||||
|
if(@socket)
|
||||||
|
@socket.send('#timeout')
|
||||||
|
#sleep one more second to ensure that the message reaches the submissions_controller.
|
||||||
|
sleep(1)
|
||||||
|
@socket.close
|
||||||
|
end
|
||||||
kill_container(container)
|
kill_container(container)
|
||||||
end
|
end
|
||||||
#ensure
|
#ensure
|
||||||
@ -274,6 +280,7 @@ class DockerClient
|
|||||||
Rails.logger.debug('exiting container ' + container.to_s)
|
Rails.logger.debug('exiting container ' + container.to_s)
|
||||||
# exit the timeout thread if it is still alive
|
# exit the timeout thread if it is still alive
|
||||||
exit_thread_if_alive
|
exit_thread_if_alive
|
||||||
|
@socket.close
|
||||||
# if we use pooling and recylce the containers, put it back. otherwise, destroy it.
|
# if we use pooling and recylce the containers, put it back. otherwise, destroy it.
|
||||||
(DockerContainerPool.config[:active] && RECYCLE_CONTAINERS) ? self.class.return_container(container, @execution_environment) : self.class.destroy_container(container)
|
(DockerContainerPool.config[:active] && RECYCLE_CONTAINERS) ? self.class.return_container(container, @execution_environment) : self.class.destroy_container(container)
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user