Persist TestrunMessages and store timestamp
So far, the Testrun messages are in addition to the Tesstrun.output column
This commit is contained in:
@ -8,7 +8,8 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
def strip_strings
|
||||
# trim whitespace from beginning and end of string attributes
|
||||
# except for the `content` of CodeOcean::Files
|
||||
attribute_names.without('content').each do |name|
|
||||
# and except the `log` of TestrunMessages or the `output` of Testruns
|
||||
attribute_names.without('content', 'log', 'output').each do |name|
|
||||
if send(name.to_sym).respond_to?(:strip)
|
||||
send("#{name}=".to_sym, send(name).strip)
|
||||
end
|
||||
|
@ -62,7 +62,7 @@ class Runner < ApplicationRecord
|
||||
# initializing its Runner::Connection with the given event loop. The Runner::Connection class ensures that
|
||||
# this event loop is stopped after the socket was closed.
|
||||
event_loop = Runner::EventLoop.new
|
||||
socket = @strategy.attach_to_execution(command, event_loop, &block)
|
||||
socket = @strategy.attach_to_execution(command, event_loop, starting_time, &block)
|
||||
event_loop.wait
|
||||
raise socket.error if socket.error.present?
|
||||
rescue Runner::Error => e
|
||||
@ -74,30 +74,34 @@ class Runner < ApplicationRecord
|
||||
end
|
||||
|
||||
def execute_command(command, raise_exception: true)
|
||||
output = {}
|
||||
stdout = +''
|
||||
stderr = +''
|
||||
output = {
|
||||
stdout: +'',
|
||||
stderr: +'',
|
||||
messages: [],
|
||||
exit_code: 1, # default to error
|
||||
}
|
||||
try = 0
|
||||
|
||||
exit_code = 1 # default to error
|
||||
begin
|
||||
if try.nonzero?
|
||||
request_new_id
|
||||
save
|
||||
end
|
||||
|
||||
execution_time = attach_to_execution(command) do |socket|
|
||||
execution_time = attach_to_execution(command) do |socket, starting_time|
|
||||
socket.on :stderr do |data|
|
||||
stderr << data
|
||||
output[:stderr] << data
|
||||
output[:messages].push({cmd: :write, stream: :stderr, log: data, timestamp: Time.zone.now - starting_time})
|
||||
end
|
||||
socket.on :stdout do |data|
|
||||
stdout << data
|
||||
output[:stdout] << data
|
||||
output[:messages].push({cmd: :write, stream: :stdout, log: data, timestamp: Time.zone.now - starting_time})
|
||||
end
|
||||
socket.on :exit do |received_exit_code|
|
||||
exit_code = received_exit_code
|
||||
output[:exit_code] = received_exit_code
|
||||
end
|
||||
end
|
||||
output.merge!(container_execution_time: execution_time, status: exit_code.zero? ? :ok : :failed)
|
||||
output.merge!(container_execution_time: execution_time, status: output[:exit_code].zero? ? :ok : :failed)
|
||||
rescue Runner::Error::ExecutionTimeout => e
|
||||
Rails.logger.debug { "Running command `#{command}` timed out: #{e.message}" }
|
||||
output.merge!(status: :timeout, container_execution_time: e.execution_duration)
|
||||
@ -122,8 +126,7 @@ class Runner < ApplicationRecord
|
||||
raise e if raise_exception && defined?(e) && e.present?
|
||||
|
||||
# If the process was killed with SIGKILL, it is most likely that the OOM killer was triggered.
|
||||
output[:status] = :out_of_memory if exit_code == 137
|
||||
output.merge!(stdout: stdout, stderr: stderr, exit_code: exit_code)
|
||||
output[:status] = :out_of_memory if output[:exit_code] == 137
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -258,6 +258,7 @@ class Submission < ApplicationRecord
|
||||
container_execution_time: output[:container_execution_time],
|
||||
waiting_for_container_time: output[:waiting_for_container_time]
|
||||
)
|
||||
TestrunMessage.create_for(testrun, output[:messages])
|
||||
TestrunExecutionEnvironment.create(testrun: testrun, execution_environment: @used_execution_environment)
|
||||
|
||||
filename = file.filepath
|
||||
|
@ -12,6 +12,7 @@ class Testrun < ApplicationRecord
|
||||
container_depleted: 2,
|
||||
timeout: 3,
|
||||
out_of_memory: 4,
|
||||
client_kill: 5,
|
||||
}, _default: :ok, _prefix: true
|
||||
|
||||
validates :exit_code, numericality: {only_integer: true, min: 0, max: 255}, allow_nil: true
|
||||
|
@ -18,6 +18,7 @@ class TestrunMessage < ApplicationRecord
|
||||
client_kill: 11,
|
||||
exception: 12,
|
||||
result: 13,
|
||||
canvasevent: 14,
|
||||
}, _default: :write, _prefix: true
|
||||
|
||||
enum stream: {
|
||||
@ -28,9 +29,28 @@ class TestrunMessage < ApplicationRecord
|
||||
|
||||
validates :cmd, presence: true
|
||||
validates :timestamp, presence: true
|
||||
|
||||
validates :stream, length: {minimum: 0, allow_nil: false}, if: -> { cmd_write? }
|
||||
validates :log, length: {minimum: 0, allow_nil: false}, if: -> { cmd_write? }
|
||||
validate :either_data_or_log
|
||||
|
||||
def self.create_for(testrun, messages)
|
||||
messages.map! do |message|
|
||||
# We create a new hash and move all known keys
|
||||
result = {}
|
||||
result[:testrun] = testrun
|
||||
result[:log] = message.delete(:log) || (message.delete(:data) if message[:cmd] == :write)
|
||||
result[:timestamp] = message.delete :timestamp
|
||||
result[:stream] = message.delete :stream
|
||||
result[:cmd] = message.delete :cmd
|
||||
# The remaining keys will be stored in the `data` column
|
||||
result[:data] = message.presence
|
||||
result
|
||||
end
|
||||
|
||||
# An array with hashes is passed, all are stored
|
||||
TestrunMessage.create!(messages)
|
||||
end
|
||||
|
||||
def either_data_or_log
|
||||
if [data, log].count(&:present?) > 1
|
||||
errors.add(log, "can't be present if data is also present")
|
||||
|
Reference in New Issue
Block a user