fixed some errors concerning pooling, container cleanup, timeouts etc.
This commit is contained in:
@ -4,6 +4,7 @@ require 'pathname'
|
|||||||
class DockerClient
|
class DockerClient
|
||||||
CONTAINER_WORKSPACE_PATH = '/workspace'
|
CONTAINER_WORKSPACE_PATH = '/workspace'
|
||||||
DEFAULT_MEMORY_LIMIT = 256
|
DEFAULT_MEMORY_LIMIT = 256
|
||||||
|
# Ralf: I suggest to replace this with the environment variable. Ask Hauke why this is not the case!
|
||||||
LOCAL_WORKSPACE_ROOT = Rails.root.join('tmp', 'files', Rails.env)
|
LOCAL_WORKSPACE_ROOT = Rails.root.join('tmp', 'files', Rails.env)
|
||||||
MINIMUM_MEMORY_LIMIT = 4
|
MINIMUM_MEMORY_LIMIT = 4
|
||||||
RECYCLE_CONTAINERS = true
|
RECYCLE_CONTAINERS = true
|
||||||
@ -17,6 +18,14 @@ class DockerClient
|
|||||||
raise(Error, "The Docker host at #{Docker.url} is not reachable!")
|
raise(Error, "The Docker host at #{Docker.url} is not reachable!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.clean_container_workspace(container)
|
||||||
|
local_workspace_path = local_workspace_path(container)
|
||||||
|
if local_workspace_path && Pathname.new(local_workspace_path).exist?
|
||||||
|
Pathname.new(local_workspace_path).children.each{ |p| p.rmtree}
|
||||||
|
FileUtils.rmdir(Pathname.new(local_workspace_path))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def command_substitutions(filename)
|
def command_substitutions(filename)
|
||||||
{class_name: File.basename(filename, File.extname(filename)).camelize, filename: filename, module_name: File.basename(filename, File.extname(filename)).underscore}
|
{class_name: File.basename(filename, File.extname(filename)).camelize, filename: filename, module_name: File.basename(filename, File.extname(filename)).underscore}
|
||||||
end
|
end
|
||||||
@ -51,6 +60,7 @@ class DockerClient
|
|||||||
tries ||= 0
|
tries ||= 0
|
||||||
container = Docker::Container.create(container_creation_options(execution_environment))
|
container = Docker::Container.create(container_creation_options(execution_environment))
|
||||||
local_workspace_path = generate_local_workspace_path
|
local_workspace_path = generate_local_workspace_path
|
||||||
|
# container.start always creates the passed local_workspace_path on disk. Seems like we have to live with that, therefore we can also just create the empty folder ourselves.
|
||||||
FileUtils.mkdir(local_workspace_path)
|
FileUtils.mkdir(local_workspace_path)
|
||||||
container.start(container_start_options(execution_environment, local_workspace_path))
|
container.start(container_start_options(execution_environment, local_workspace_path))
|
||||||
container.start_time = Time.now
|
container.start_time = Time.now
|
||||||
@ -61,8 +71,8 @@ class DockerClient
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create_workspace_files(container, submission)
|
def create_workspace_files(container, submission)
|
||||||
#clear directory (it should be emtpy anyhow)
|
#clear directory (it should be empty anyhow)
|
||||||
Pathname.new(self.class.local_workspace_path(container)).children.each{ |p| p.rmtree}
|
#Pathname.new(self.class.local_workspace_path(container)).children.each{ |p| p.rmtree}
|
||||||
submission.collect_files.each do |file|
|
submission.collect_files.each do |file|
|
||||||
FileUtils.mkdir_p(File.join(self.class.local_workspace_path(container), file.path || ''))
|
FileUtils.mkdir_p(File.join(self.class.local_workspace_path(container), file.path || ''))
|
||||||
if file.file_type.binary?
|
if file.file_type.binary?
|
||||||
@ -85,10 +95,7 @@ class DockerClient
|
|||||||
Rails.logger.info('destroying container ' + container.to_s)
|
Rails.logger.info('destroying container ' + container.to_s)
|
||||||
container.stop.kill
|
container.stop.kill
|
||||||
container.port_bindings.values.each { |port| PortPool.release(port) }
|
container.port_bindings.values.each { |port| PortPool.release(port) }
|
||||||
local_workspace_path = local_workspace_path(container)
|
clean_container_workspace(container)
|
||||||
if local_workspace_path && Pathname.new(local_workspace_path).exist?
|
|
||||||
Pathname.new(local_workspace_path).children.each{ |p| p.rmtree}
|
|
||||||
end
|
|
||||||
container.delete(force: true, v: true)
|
container.delete(force: true, v: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -157,6 +164,7 @@ class DockerClient
|
|||||||
|
|
||||||
def self.mapped_directories(local_workspace_path)
|
def self.mapped_directories(local_workspace_path)
|
||||||
remote_workspace_path = local_workspace_path.sub(LOCAL_WORKSPACE_ROOT.to_s, config[:workspace_root])
|
remote_workspace_path = local_workspace_path.sub(LOCAL_WORKSPACE_ROOT.to_s, config[:workspace_root])
|
||||||
|
# create the string to be returned
|
||||||
["#{remote_workspace_path}:#{CONTAINER_WORKSPACE_PATH}"]
|
["#{remote_workspace_path}:#{CONTAINER_WORKSPACE_PATH}"]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -170,38 +178,40 @@ class DockerClient
|
|||||||
`docker pull #{docker_image}` if docker_image
|
`docker pull #{docker_image}` if docker_image
|
||||||
end
|
end
|
||||||
|
|
||||||
def return_container(container)
|
def self.return_container(container, execution_environment)
|
||||||
local_workspace_path = self.class.local_workspace_path(container)
|
clean_container_workspace(container)
|
||||||
Pathname.new(local_workspace_path).children.each{ |p| p.rmtree}
|
DockerContainerPool.return_container(container, execution_environment)
|
||||||
DockerContainerPool.return_container(container, @execution_environment)
|
|
||||||
end
|
end
|
||||||
private :return_container
|
#private :return_container
|
||||||
|
|
||||||
def send_command(command, container, &block)
|
def send_command(command, container, &block)
|
||||||
|
result = {status: :failed, stdout: '', stderr: ''}
|
||||||
Timeout.timeout(@execution_environment.permitted_execution_time.to_i) do
|
Timeout.timeout(@execution_environment.permitted_execution_time.to_i) do
|
||||||
output = container.exec(['bash', '-c', command])
|
output = container.exec(['bash', '-c', command])
|
||||||
Rails.logger.info "output from container.exec"
|
Rails.logger.info "output from container.exec"
|
||||||
Rails.logger.info output
|
Rails.logger.info output
|
||||||
{status: output[2] == 0 ? :ok : :failed, stdout: output[0].join, stderr: output[1].join}
|
result = {status: output[2] == 0 ? :ok : :failed, stdout: output[0].join, stderr: output[1].join}
|
||||||
end
|
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)
|
||||||
|
result
|
||||||
rescue Timeout::Error
|
rescue Timeout::Error
|
||||||
timeout_occured = true
|
timeout_occured = true
|
||||||
Rails.logger.info('got timeout error for container ' + container.to_s)
|
Rails.logger.info('got timeout error for container ' + container.to_s)
|
||||||
#container.restart if RECYCLE_CONTAINERS
|
|
||||||
|
# remove container from pool, then destroy it
|
||||||
DockerContainerPool.remove_from_all_containers(container, @execution_environment)
|
DockerContainerPool.remove_from_all_containers(container, @execution_environment)
|
||||||
|
|
||||||
# destroy container
|
# destroy container
|
||||||
self.class.destroy_container(container)
|
self.class.destroy_container(container)
|
||||||
|
|
||||||
|
# if we recylce containers, we start a fresh one
|
||||||
if(RECYCLE_CONTAINERS)
|
if(RECYCLE_CONTAINERS)
|
||||||
# create new container and add it to @all_containers. will be added to @containers on return_container
|
# create new container and add it to @all_containers and @containers.
|
||||||
container = self.class.create_container(@execution_environment)
|
container = self.class.create_container(@execution_environment)
|
||||||
DockerContainerPool.add_to_all_containers(container, @execution_environment)
|
DockerContainerPool.add_to_all_containers(container, @execution_environment)
|
||||||
end
|
end
|
||||||
{status: :timeout}
|
{status: :timeout}
|
||||||
ensure
|
|
||||||
Rails.logger.info('send_command ensuring for' + container.to_s)
|
|
||||||
RECYCLE_CONTAINERS ? return_container(container) : self.class.destroy_container(container)
|
|
||||||
end
|
end
|
||||||
private :send_command
|
private :send_command
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user