diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index a3e2add7..27d7a5d6 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -68,9 +68,7 @@ class SubmissionsController < ApplicationController end def download - if @embed_options[:disable_download] - raise Pundit::NotAuthorizedError - end + raise Pundit::NotAuthorizedError if @embed_options[:disable_download] # files = @submission.files.map{ } # zipline( files, 'submission.zip') @@ -112,9 +110,7 @@ class SubmissionsController < ApplicationController end def download_file - if @embed_options[:disable_download] - raise Pundit::NotAuthorizedError - end + raise Pundit::NotAuthorizedError if @embed_options[:disable_download] if @file.native_file? send_file(@file.native_file.path) @@ -142,7 +138,7 @@ class SubmissionsController < ApplicationController @output = '' socket.on :output do |data| - Rails.logger.info("#{Time.zone.now.getutc}: Docker sending: #{data}") + Rails.logger.info("#{Time.zone.now.getutc}: Container sending: #{data}") @output << data if @output.size + data.size <= max_output_buffer_size end @@ -161,38 +157,36 @@ class SubmissionsController < ApplicationController tubesock.send_data JSON.dump({cmd: :timeout}) @output = "timeout: #{@output}" elsif @output.empty? - tubesock.send_data JSON.dump({cmd: :write, stream: :stdout, data: t('exercises.implement.no_output', timestamp: l(Time.now, format: :short)) + "\n"}) + tubesock.send_data JSON.dump({cmd: :write, stream: :stdout, data: "#{t('exercises.implement.no_output', timestamp: l(Time.zone.now, format: :short))}\n"}) end - tubesock.send_data JSON.dump({cmd: :write, stream: :stdout, data: t('exercises.implement.exit', exit_code: exit_code) + "\n"}) unless status == :timeouted + tubesock.send_data JSON.dump({cmd: :write, stream: :stdout, data: "#{t('exercises.implement.exit', exit_code: exit_code)}\n"}) unless status == :timeouted kill_socket(tubesock) end tubesock.onmessage do |event| - begin - event = JSON.parse(event).deep_symbolize_keys - case event[:cmd].to_sym - when :client_kill - EventMachine.stop_event_loop - kill_socket(tubesock) - container.destroy - Rails.logger.debug('Client exited container.') - when :result - socket.send event[:data] - else - Rails.logger.info("Unknown command from client: #{event[:cmd]}") - end - rescue JSON::ParserError => error - Rails.logger.debug { "Data received from client is not valid json: #{data}" } - Sentry.set_extras(data: data) - rescue TypeError => error - Rails.logger.debug { "JSON data received from client cannot be parsed to hash: #{data}" } - Sentry.set_extras(data: data) + event = JSON.parse(event).deep_symbolize_keys + case event[:cmd].to_sym + when :client_kill + EventMachine.stop_event_loop + kill_socket(tubesock) + container.destroy + Rails.logger.debug('Client exited container.') + when :result + socket.send event[:data] + else + Rails.logger.info("Unknown command from client: #{event[:cmd]}") end + rescue JSON::ParserError => e + ails.logger.debug { "Data received from client is not valid json: #{data}" } + Sentry.set_extras(data: data) + rescue TypeError => e + Rails.logger.debug { "JSON data received from client cannot be parsed to hash: #{data}" } + Sentry.set_extras(data: data) end end def run - # TODO do we need this thread? If so, how to fix double render? (to reproduce: remove .join and run) + # TODO: do we need this thread? If so, how to fix double render? (to reproduce: remove .join and run) Thread.new do hijack do |tubesock| if @embed_options[:disable_run] @@ -208,7 +202,7 @@ class SubmissionsController < ApplicationController ensure ActiveRecord::Base.connection_pool.release_connection end.join - # TODO determine if this is necessary + # TODO: determine if this is necessary # unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive? # Thread.new do # EventMachine.run @@ -248,9 +242,7 @@ class SubmissionsController < ApplicationController if @output.present? @submission.exercise.execution_environment.error_templates.each do |template| pattern = Regexp.new(template.signature).freeze - if pattern.match(@output) - results << StructuredError.create_from_template(template, @output, @submission) - end + results << StructuredError.create_from_template(template, @output, @submission) if pattern.match(@output) end end results @@ -288,7 +280,7 @@ class SubmissionsController < ApplicationController # private :set_docker_client def set_file - @file = @files.detect {|file| file.name_with_extension == sanitize_filename } + @file = @files.detect { |file| file.name_with_extension == sanitize_filename } head :not_found unless @file end private :set_file @@ -314,7 +306,7 @@ class SubmissionsController < ApplicationController def statistics; end - # TODO is this needed? + # TODO: is this needed? # def test # Thread.new do # hijack do |tubesock| @@ -332,7 +324,6 @@ class SubmissionsController < ApplicationController # end # end - def with_server_sent_events response.headers['Content-Type'] = 'text/event-stream' server_sent_event = SSE.new(response.stream) diff --git a/lib/runner/runner.rb b/lib/runner/runner.rb index aa5fa7bc..dbaa8a5c 100644 --- a/lib/runner/runner.rb +++ b/lib/runner/runner.rb @@ -2,24 +2,22 @@ class Runner BASE_URL = CodeOcean::Config.new(:code_ocean).read[:container_management][:url] - HEADERS = {"Content-Type" => "application/json"} + HEADERS = {'Content-Type' => 'application/json'}.freeze attr_accessor :waiting_time def initialize(execution_environment, time_limit = nil) url = "#{BASE_URL}/runners" body = {executionEnvironmentId: execution_environment.id} - if time_limit - body[:timeLimit] = time_limit - end + body[:timeLimit] = time_limit if time_limit response = Faraday.post(url, body.to_json, HEADERS) response = parse response @id = response[:runnerId] end def copy_files(files) - url = runner_url + "/files" - body = { files: files.map { |filename, content| { filepath: filename, content: content } } } + url = "#{runner_url}/files" + body = {files: files.map { |filename, content| {filepath: filename, content: content} }} Faraday.patch(url, body.to_json, HEADERS) end @@ -32,20 +30,19 @@ class Runner end def execute_command(command) - url = runner_url + "/execute" + url = "#{runner_url}/execute" response = Faraday.post(url, {command: command}.to_json, HEADERS) - response = parse response - response + parse response end def execute_interactively(command) - starting_time = Time.now + starting_time = Time.zone.now websocket_url = execute_command(command)[:websocketUrl] EventMachine.run do socket = RunnerConnection.new(websocket_url) yield(self, socket) if block_given? end - Time.now - starting_time # execution time + Time.zone.now - starting_time # execution time end def destroy diff --git a/lib/runner/runner_connection.rb b/lib/runner/runner_connection.rb index 415c282b..ae654dd1 100644 --- a/lib/runner/runner_connection.rb +++ b/lib/runner/runner_connection.rb @@ -1,19 +1,21 @@ +# frozen_string_literal: true + require 'faye/websocket/client' require 'json_schemer' class RunnerConnection EVENTS = %i[start output exit stdout stderr].freeze - BACKEND_OUTPUT_SCHEMA = JSONSchemer.schema(JSON.parse(File.read("lib/runner/backend-output.schema.json"))) + BACKEND_OUTPUT_SCHEMA = JSONSchemer.schema(JSON.parse(File.read('lib/runner/backend-output.schema.json'))) def initialize(url) @socket = Faye::WebSocket::Client.new(url, [], ping: 5) %i[open message error close].each do |event_type| - @socket.on event_type do |event| __send__(:"on_#{event_type}", event) end + @socket.on(event_type) { |event| __send__(:"on_#{event_type}", event) } end - EVENTS.each { |event_type| instance_variable_set(:"@#{event_type}_callback", lambda {|e|}) } - @start_callback = lambda {} + EVENTS.each { |event_type| instance_variable_set(:"@#{event_type}_callback", ->(e) {}) } + @start_callback = -> {} @exit_code = 0 end @@ -42,7 +44,7 @@ class RunnerConnection event = decode(event.data) - # TODO handle other events like timeout + # TODO: handle other events like timeout case event[:type].to_sym when :exit_code @exit_code = event[:data] @@ -57,14 +59,13 @@ class RunnerConnection end end - def on_open(event) + def on_open(_event) @start_callback.call end - def on_error(event) - end + def on_error(event); end - def on_close(event) + def on_close(_event) @exit_callback.call @exit_code end -end \ No newline at end of file +end