diff --git a/Gemfile b/Gemfile index 92afc60f..073f6d8a 100644 --- a/Gemfile +++ b/Gemfile @@ -17,6 +17,7 @@ gem 'i18n-js' gem 'ims-lti', '< 2.0.0' gem 'jbuilder' gem 'js-routes' +gem 'json_schemer' gem 'kramdown' gem 'mimemagic' gem 'nokogiri' diff --git a/Gemfile.lock b/Gemfile.lock index d9309fff..f0efcde2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -157,6 +157,8 @@ GEM multi_json domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) + ecma-re-validator (0.3.0) + regexp_parser (~> 2.0) erubi (1.10.0) eventmachine (1.2.7) excon (0.87.0) @@ -194,6 +196,7 @@ GEM haml (5.2.2) temple (>= 0.8.0) tilt + hana (1.3.7) hashdiff (1.0.1) headless (2.3.1) highline (2.0.3) @@ -222,6 +225,11 @@ GEM js-routes (2.1.1) railties (>= 4) json (2.3.1) + json_schemer (0.2.18) + ecma-re-validator (~> 0.3) + hana (~> 1.3) + regexp_parser (~> 2.0) + uri_template (~> 0.7) jwt (2.3.0) kaminari (1.2.1) activesupport (>= 4.1.0) @@ -507,6 +515,7 @@ GEM unf_ext unf_ext (0.0.8) unicode-display_width (2.1.0) + uri_template (0.7.0) web-console (4.1.0) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -561,6 +570,7 @@ DEPENDENCIES ims-lti (< 2.0.0) jbuilder js-routes + json_schemer kramdown listen mimemagic diff --git a/lib/runner/backend-output.schema.json b/lib/runner/backend-output.schema.json new file mode 100644 index 00000000..a256846e --- /dev/null +++ b/lib/runner/backend-output.schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "http://json-schema.org/schema#", + "title": "event", + "type": "object", + "oneOf": [ + { + "properties": { + "type": { + "const": "exit", + "required": true + }, + "data": { + "type": "integer", + "required": true, + "minimum": 0, + "maximum": 255 + } + }, + "additionalProperties": false + }, + { + "properties": { + "type": { + "enum": [ "stdout", "stderr", "error" ], + "required": true + }, + "data": { + "type": "string", + "required": true + } + }, + "additionalProperties": false + }, + { + "properties": { + "type": { + "enum": [ "start", "timeout" ], + "required": true + } + }, + "additionalProperties": false + } + ] +} \ No newline at end of file diff --git a/lib/runner.rb b/lib/runner/runner.rb similarity index 74% rename from lib/runner.rb rename to lib/runner/runner.rb index 4ef77a8a..aa5fa7bc 100644 --- a/lib/runner.rb +++ b/lib/runner/runner.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'runner_connection' - class Runner BASE_URL = CodeOcean::Config.new(:code_ocean).read[:container_management][:url] HEADERS = {"Content-Type" => "application/json"} @@ -10,19 +8,19 @@ class Runner def initialize(execution_environment, time_limit = nil) url = "#{BASE_URL}/runners" - body = {execution_environment_id: execution_environment.id} + body = {executionEnvironmentId: execution_environment.id} if time_limit - body[:time_limit] = time_limit + body[:timeLimit] = time_limit end response = Faraday.post(url, body.to_json, HEADERS) response = parse response - @id = response[:id] + @id = response[:runnerId] end def copy_files(files) url = runner_url + "/files" - body = { files: files.map{ |filename, content| { filename: filename, content: content } } } - Faraday.post(url, body.to_json, HEADERS) + body = { files: files.map { |filename, content| { filepath: filename, content: content } } } + Faraday.patch(url, body.to_json, HEADERS) end def copy_submission_files(submission) @@ -42,7 +40,7 @@ class Runner def execute_interactively(command) starting_time = Time.now - websocket_url = execute_command(command)[:websocket_url] + websocket_url = execute_command(command)[:websocketUrl] EventMachine.run do socket = RunnerConnection.new(websocket_url) yield(self, socket) if block_given? @@ -55,7 +53,9 @@ class Runner end def status - parse(Faraday.get(runner_url))[:status].to_sym + # parse(Faraday.get(runner_url))[:status].to_sym + # TODO return actual state retrieved via websocket + :timeouted end private diff --git a/lib/runner_connection.rb b/lib/runner/runner_connection.rb similarity index 84% rename from lib/runner_connection.rb rename to lib/runner/runner_connection.rb index 25008d66..415c282b 100644 --- a/lib/runner_connection.rb +++ b/lib/runner/runner_connection.rb @@ -1,7 +1,9 @@ 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"))) def initialize(url) @socket = Faye::WebSocket::Client.new(url, [], ping: 5) @@ -36,7 +38,11 @@ class RunnerConnection end def on_message(event) + return unless BACKEND_OUTPUT_SCHEMA.valid?(JSON.parse(event.data)) + event = decode(event.data) + + # TODO handle other events like timeout case event[:type].to_sym when :exit_code @exit_code = event[:data]