Rescue RuntimeError (FayeWebsocket) and apply style

This commit is contained in:
Sebastian Serth
2020-05-05 22:46:28 +02:00
parent 278d48ca6c
commit 99979eeb4f

View File

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'concurrent'
require 'pathname'
@ -31,9 +33,9 @@ class DockerClient
if local_workspace_path && Pathname.new(local_workspace_path).exist?
Pathname.new(local_workspace_path).children.each do |p|
p.rmtree
rescue Errno::ENOENT, Errno::EACCES => error
Raven.capture_exception(error)
Rails.logger.error("clean_container_workspace: Got #{error.class.to_s}: #{error.to_s}")
rescue Errno::ENOENT, Errno::EACCES => e
Raven.capture_exception(e)
Rails.logger.error("clean_container_workspace: Got #{e.class}: #{e}")
end
# FileUtils.rmdir(Pathname.new(local_workspace_path))
end
@ -78,7 +80,7 @@ class DockerClient
end
def create_socket(container, stderr = false)
# todo factor out query params
# TODO: factor out query params
# todo separate stderr
query_params = 'logs=0&stream=1&' + (stderr ? 'stderr=1' : 'stdout=1&stdin=1')
@ -86,18 +88,18 @@ class DockerClient
headers = {'Origin' => 'http://localhost'}
socket_url = DockerClient.config['ws_host'] + '/v1.27/containers/' + @container.id + '/attach/ws?' + query_params
socket = Faye::WebSocket::Client.new(socket_url, [], :headers => headers)
socket = Faye::WebSocket::Client.new(socket_url, [], headers: headers)
Rails.logger.debug "Opening Websocket on URL " + socket_url
Rails.logger.debug 'Opening Websocket on URL ' + socket_url
socket.on :error do |event|
Rails.logger.info "Websocket error: " + event.message.to_s
Rails.logger.info 'Websocket error: ' + event.message.to_s
end
socket.on :close do |event|
Rails.logger.info "Websocket closed."
socket.on :close do |_event|
Rails.logger.info 'Websocket closed.'
end
socket.on :open do |event|
Rails.logger.info "Websocket created."
socket.on :open do |_event|
Rails.logger.info 'Websocket created.'
kill_after_timeout(container)
end
socket
@ -145,8 +147,8 @@ class DockerClient
end
container
rescue Docker::Error::NotFoundError => error
Rails.logger.error('create_container: Got Docker::Error::NotFoundError: ' + error.to_s)
rescue Docker::Error::NotFoundError => e
Rails.logger.error('create_container: Got Docker::Error::NotFoundError: ' + e.to_s)
destroy_container(container)
# (tries += 1) <= RETRY_COUNT ? retry : raise(error)
end
@ -163,8 +165,8 @@ class DockerClient
end
end
FileUtils.chmod_R('+rwX', self.class.local_workspace_path(container))
rescue Docker::Error::NotFoundError => error
Rails.logger.info('create_workspace_files: Rescued from Docker::Error::NotFoundError: ' + error.to_s)
rescue Docker::Error::NotFoundError => e
Rails.logger.info('create_workspace_files: Rescued from Docker::Error::NotFoundError: ' + e.to_s)
end
private :create_workspace_files
@ -179,23 +181,21 @@ class DockerClient
private :create_workspace_file
def create_workspace_files_transmit(container, submission)
begin
# create a temporary dir, put all files in it, and put it into the container. the dir is automatically removed when leaving the block.
Dir.mktmpdir { |dir|
Dir.mktmpdir do |dir|
submission.collect_files.each do |file|
disk_file = File.new(dir + '/' + (file.path || '') + file.name_with_extension, 'w')
disk_file.write(file.content)
disk_file.close
end
begin
# create target folder, TODO re-active this when we remove shared folder bindings
# container.exec(['bash', '-c', 'mkdir ' + CONTAINER_WORKSPACE_PATH])
# container.exec(['bash', '-c', 'chown -R python ' + CONTAINER_WORKSPACE_PATH])
# container.exec(['bash', '-c', 'chgrp -G python ' + CONTAINER_WORKSPACE_PATH])
rescue StandardError => error
Rails.logger.error('create workspace folder: Rescued from StandardError: ' + error.to_s)
rescue StandardError => e
Rails.logger.error('create workspace folder: Rescued from StandardError: ' + e.to_s)
end
# sleep 1000
@ -203,9 +203,8 @@ class DockerClient
begin
# tar the files in dir and put the tar to CONTAINER_WORKSPACE_PATH in the container
container.archive_in(dir, CONTAINER_WORKSPACE_PATH, overwrite: false)
rescue StandardError => error
Rails.logger.error('insert tar: Rescued from StandardError: ' + error.to_s)
rescue StandardError => e
Rails.logger.error('insert tar: Rescued from StandardError: ' + e.to_s)
end
# Rails.logger.info('command: tar -xf ' + CONTAINER_WORKSPACE_PATH + '/' + dir.split('/tmp/')[1] + ' -C ' + CONTAINER_WORKSPACE_PATH)
@ -213,23 +212,18 @@ class DockerClient
begin
# untar the tar file placed in the CONTAINER_WORKSPACE_PATH
container.exec(['bash', '-c', 'tar -xf ' + CONTAINER_WORKSPACE_PATH + '/' + dir.split('/tmp/')[1] + ' -C ' + CONTAINER_WORKSPACE_PATH])
rescue StandardError => error
Rails.logger.error('untar: Rescued from StandardError: ' + error.to_s)
rescue StandardError => e
Rails.logger.error('untar: Rescued from StandardError: ' + e.to_s)
end
# sleep 1000
}
rescue StandardError => error
Rails.logger.error('create_workspace_files_transmit: Rescued from StandardError: ' + error.to_s)
end
rescue StandardError => e
Rails.logger.error('create_workspace_files_transmit: Rescued from StandardError: ' + e.to_s)
end
def self.destroy_container(container)
if @socket
@socket.close
end
@socket&.close
Rails.logger.info('destroying container ' + container.to_s)
# Checks only if container assignment is not nil and not whether the container itself is still present.
@ -241,11 +235,11 @@ class DockerClient
elsif container
DockerContainerPool.destroy_container(container)
end
rescue Docker::Error::NotFoundError => error
Rails.logger.error('destroy_container: Rescued from Docker::Error::NotFoundError: ' + error.to_s)
rescue Docker::Error::NotFoundError => e
Rails.logger.error('destroy_container: Rescued from Docker::Error::NotFoundError: ' + e.to_s)
Rails.logger.error('No further actions are done concerning that.')
rescue Docker::Error::ConflictError => error
Rails.logger.error('destroy_container: Rescued from Docker::Error::ConflictError: ' + error.to_s)
rescue Docker::Error::ConflictError => e
Rails.logger.error('destroy_container: Rescued from Docker::Error::ConflictError: ' + e.to_s)
Rails.logger.error('No further actions are done concerning that.')
end
@ -274,14 +268,14 @@ class DockerClient
else
{status: :container_depleted, waiting_for_container_time: waiting_for_container_time, container_execution_time: nil}
end
rescue Excon::Errors::SocketError => error
rescue Excon::Errors::SocketError => e
# socket errors seems to be normal when using exec
# so lets ignore them for now
# (tries += 1) <= RETRY_COUNT ? retry : raise(error)
end
# called when the user clicks the "Run" button
def open_websocket_connection(command, before_execution_block, output_consuming_block)
def open_websocket_connection(command, before_execution_block, _output_consuming_block)
@container = DockerContainerPool.get_container(@execution_environment)
if @container
@container.status = :executing
@ -289,8 +283,8 @@ class DockerClient
# before_execution_block.try(:call)
begin
before_execution_block.call
rescue StandardError => error
Rails.logger.error('execute_websocket_command: Rescued from StandardError caused by before_execution_block.call: ' + error.to_s)
rescue StandardError => e
Rails.logger.error('execute_websocket_command: Rescued from StandardError caused by before_execution_block.call: ' + e.to_s)
end
# TODO: catch exception if socket could not be created
@socket ||= create_socket(@container)
@ -312,14 +306,16 @@ class DockerClient
Rails.logger.info('Killing container after timeout of ' + timeout.to_s + ' seconds.')
# send timeout to the tubesock socket
# FIXME: 2nd thread to notify user.
if @tubesock
@tubesock.send_data JSON.dump({'cmd' => 'timeout'})
end
@tubesock&.send_data JSON.dump({'cmd' => 'timeout'})
if @socket
begin
@socket.send('#timeout')
# sleep one more second to ensure that the message reaches the submissions_controller.
sleep(1)
@socket.close
rescue RuntimeError => e
Rails.logger.error(e)
end
end
Thread.new do
kill_container(container)
@ -334,9 +330,7 @@ class DockerClient
end
def exit_thread_if_alive
if (@thread && @thread.alive?)
@thread.exit
end
@thread.exit if @thread&.alive?
end
def exit_container(container)
@ -345,10 +339,10 @@ class DockerClient
exit_thread_if_alive
@socket.close
# 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
def kill_container(container, create_new = true)
def kill_container(container, _create_new = true)
exit_thread_if_alive
Rails.logger.info('killing container ' + container.to_s)
self.class.destroy_container(container)
@ -376,16 +370,14 @@ class DockerClient
end
def self.find_image_by_tag(tag)
# todo: cache this.
# TODO: cache this.
Docker::Image.all.detect do |image|
begin
image.info['RepoTags'].flatten.include?(tag)
rescue
rescue StandardError
# Skip image if it is not tagged
next
end
end
end
def self.generate_local_workspace_path
File.join(LOCAL_WORKSPACE_ROOT, SecureRandom.uuid)
@ -397,7 +389,7 @@ class DockerClient
def initialize(options = {})
@execution_environment = options[:execution_environment]
# todo: eventually re-enable this if it is cached. But in the end, we do not need this.
# TODO: eventually re-enable this if it is cached. But in the end, we do not need this.
# docker daemon got much too much load. all not 100% necessary calls to the daemon were removed.
# @image = self.class.find_image_by_tag(@execution_environment.docker_image)
# fail(Error, "Cannot find image #{@execution_environment.docker_image}!") unless @image
@ -405,11 +397,10 @@ class DockerClient
def self.initialize_environment
# TODO: Move to DockerContainerPool
unless config[:connection_timeout] && config[:workspace_root]
fail(Error, 'Docker configuration missing!')
end
raise(Error, 'Docker configuration missing!') unless config[:connection_timeout] && config[:workspace_root]
Docker.url = config[:host] if config[:host]
# todo: availability check disabled for performance reasons. Reconsider if this is necessary.
# TODO: availability check disabled for performance reasons. Reconsider if this is necessary.
# docker daemon got much too much load. all not 100% necessary calls to the daemon were removed.
# check_availability!
FileUtils.mkdir_p(LOCAL_WORKSPACE_ROOT)
@ -445,9 +436,9 @@ class DockerClient
Rails.logger.debug('returning container ' + container.to_s)
begin
clean_container_workspace(container)
rescue Docker::Error::NotFoundError => error
rescue Docker::Error::NotFoundError => e
# FIXME: Create new container?
Rails.logger.info('return_container: Rescued from Docker::Error::NotFoundError: ' + error.to_s)
Rails.logger.info('return_container: Rescued from Docker::Error::NotFoundError: ' + e.to_s)
Rails.logger.info('Nothing is done here additionally. The container will be exchanged upon its next retrieval.')
end
DockerContainerPool.return_container(container, execution_environment)
@ -456,23 +447,23 @@ class DockerClient
# private :return_container
def send_command(command, container, &block)
def send_command(command, container)
result = {status: :failed, stdout: '', stderr: ''}
output = nil
Timeout.timeout(@execution_environment.permitted_execution_time.to_i) do
# TODO: check phusion doku again if we need -i -t options here
output = container.exec(['bash', '-c', command])
end
Rails.logger.debug "output from container.exec"
Rails.logger.debug 'output from container.exec'
Rails.logger.debug output
if output == nil
if output.nil?
kill_container(container)
else
result = {status: output[2] == 0 ? :ok : :failed, stdout: output[0].join.force_encoding('utf-8'), stderr: output[1].join.force_encoding('utf-8')}
end
# 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)
result
rescue Timeout::Error
Rails.logger.info('got timeout error for container ' + container.to_s)
@ -482,6 +473,5 @@ class DockerClient
private :send_command
class Error < RuntimeError;
end
class Error < RuntimeError; end
end