Editor: Allow file retrieval after code run

This commit is contained in:
Sebastian Serth
2022-10-04 15:17:16 +02:00
committed by Sebastian Serth
parent fb9672c7a4
commit 60078701f5
22 changed files with 311 additions and 8 deletions

View File

@ -58,7 +58,8 @@ class FileTree
disabled: !node.leaf?,
opened: !node.leaf?,
},
text: node.name,
text: name(node),
download_path: node.content.try(:download_path),
}
end
private :map_to_js_tree
@ -72,6 +73,17 @@ class FileTree
end
private :node_icon
def name(node)
# We just need any information that is only present in files retrieved from the runner's file system.
# In our case, that is the presence of the `privileged_execution` attribute.
if node.content.is_a?(CodeOcean::File) && !node.content.privileged_execution.nil?
node.content.name_with_extension_and_size
else
node.name
end
end
private :name
def to_js_tree
{
core: {

View File

@ -146,7 +146,7 @@ class Runner::Connection
@strategy.destroy_at_management
@error = Runner::Error::ExecutionTimeout.new('Execution exceeded its time limit')
when :terminated_by_codeocean, :terminated_by_management
@exit_callback.call @exit_code
@exit_callback.call @exit_code, @strategy.retrieve_files
when :terminated_by_client, :error
@strategy.destroy_at_management
else # :established

View File

@ -33,6 +33,14 @@ class Runner::Strategy
raise NotImplementedError
end
def retrieve_files(_path:, _recursive:, privileged_execution:)
raise NotImplementedError
end
def download_file(_file, privileged_execution:, &_block)
raise NotImplementedError
end
def attach_to_execution(_command, _event_loop, _starting_time, privileged_execution:)
raise NotImplementedError
end

View File

@ -104,6 +104,10 @@ class Runner::Strategy::DockerContainerPool < Runner::Strategy
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished copying files" }
end
def retrieve_files(_path:, _recursive:, privileged_execution:)
# The DockerContainerPool does not support retrieving files from the runner.
end
def attach_to_execution(command, event_loop, starting_time, privileged_execution: false) # rubocop:disable Lint/UnusedMethodArgument for the keyword argument
reset_inactivity_timer

View File

@ -25,6 +25,12 @@ class Runner::Strategy::Null < Runner::Strategy
def copy_files(_files); end
def retrieve_files(_path:, _recursive:, privileged_execution: false); end
def download_file(_file, privileged_execution: false, &_block) # rubocop:disable Lint/UnusedMethodArgument for the keyword argument
raise Runner::Error.new
end
def attach_to_execution(command, event_loop, starting_time, privileged_execution: false) # rubocop:disable Lint/UnusedMethodArgument for the keyword argument
socket = Connection.new(nil, self, event_loop)
# We don't want to return an error if the execution environment is changed

View File

@ -132,6 +132,64 @@ class Runner::Strategy::Poseidon < Runner::Strategy
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished copying files" }
end
def retrieve_files(path: './', recursive: true, privileged_execution: false)
url = "#{runner_url}/files"
params = {
path: path,
recursive: recursive,
privilegedExecution: privileged_execution || @execution_environment.privileged_execution,
}
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Retrieving files at #{runner_url} with #{params}" }
response = self.class.http_connection.get url, params
case response.status
when 200
JSON.parse(response.body)
when 424
raise Runner::Error::WorkspaceError.new("The path #{path} is not available or could not be read.")
else
self.class.handle_error response
end
rescue Faraday::Error => e
raise Runner::Error::FaradayError.new("Request to Poseidon failed: #{e.inspect}")
ensure
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished listing files" }
end
def download_file(file, privileged_execution: false, &block)
url = "#{runner_url}/files/raw"
params = {
path: file,
privilegedExecution: privileged_execution || @execution_environment.privileged_execution,
}
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Download file #{params} from #{runner_url}" }
response = self.class.new_http_connection.get url, params do |request|
content_length = nil
content_type = nil
next if block.blank?
request.options.on_data = proc do |chunk, _overall_received_bytes, env|
next unless env.success?
content_length ||= env.response_headers['Content-Length'].presence&.to_i
content_type ||= env.response_headers['Content-Type'].presence || 'application/octet-stream'
yield chunk, content_length, content_type
end
request.options
end
case response.status
when 200
response.body
when 424
raise Runner::Error::WorkspaceError.new("The file #{file} is not available or could not be read.")
else
self.class.handle_error response
end
rescue Faraday::Error => e
raise Runner::Error::FaradayError.new("Request to Poseidon failed: #{e.inspect}")
ensure
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished downloading file" }
end
def attach_to_execution(command, event_loop, starting_time, privileged_execution: false)
websocket_url = execute_command(command, privileged_execution: privileged_execution)
socket = Connection.new(websocket_url, self, event_loop)
@ -232,6 +290,12 @@ class Runner::Strategy::Poseidon < Runner::Strategy
end
end
def self.new_http_connection
Faraday.new(ssl: {ca_file: config[:ca_file]}, headers: headers) do |faraday|
faraday.adapter :net_http
end
end
def self.parse(response)
JSON.parse(response.body).deep_symbolize_keys
rescue JSON::ParserError => e