Reorder methods in strategy classes
This commit is contained in:
@ -22,7 +22,7 @@ class Runner::Connection
|
|||||||
# Internally, Faye::WebSocket uses EventMachine and the `ping` value is used to wake the EventMachine thread
|
# Internally, Faye::WebSocket uses EventMachine and the `ping` value is used to wake the EventMachine thread
|
||||||
# The `tls` option is used to customize the validation of TLS connections.
|
# The `tls` option is used to customize the validation of TLS connections.
|
||||||
# Passing `nil` as a `root_cert_file` is okay and done so for the DockerContainerPool.
|
# Passing `nil` as a `root_cert_file` is okay and done so for the DockerContainerPool.
|
||||||
@socket = Faye::WebSocket::Client.new(url, [], strategy.websocket_header.merge(ping: 0.1))
|
@socket = Faye::WebSocket::Client.new(url, [], strategy.class.websocket_header.merge(ping: 0.1))
|
||||||
@strategy = strategy
|
@strategy = strategy
|
||||||
@status = :established
|
@status = :established
|
||||||
@event_loop = event_loop
|
@event_loop = event_loop
|
||||||
|
@ -5,18 +5,10 @@ class Runner::Strategy
|
|||||||
@execution_environment = environment
|
@execution_environment = environment
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.config
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.initialize_environment
|
def self.initialize_environment
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.available_images
|
|
||||||
raise NotImplementedError
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.sync_environment(_environment)
|
def self.sync_environment(_environment)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
@ -33,11 +25,27 @@ class Runner::Strategy
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def attach_to_execution(_command)
|
def attach_to_execution(_command, _event_loop)
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
def websocket_header
|
def self.available_images
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.config
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.release
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pool_size
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.websocket_header
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,22 +3,15 @@
|
|||||||
class Runner::Strategy::DockerContainerPool < Runner::Strategy
|
class Runner::Strategy::DockerContainerPool < Runner::Strategy
|
||||||
attr_reader :container_id, :command
|
attr_reader :container_id, :command
|
||||||
|
|
||||||
def self.config
|
def initialize(runner_id, _environment)
|
||||||
# Since the docker configuration file contains code that must be executed, we use ERB templating.
|
super
|
||||||
@config ||= CodeOcean::Config.new(:docker).read(erb: true)
|
@container_id = runner_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.initialize_environment
|
def self.initialize_environment
|
||||||
DockerClient.initialize_environment unless Rails.env.test? && `which docker`.blank?
|
DockerClient.initialize_environment unless Rails.env.test? && `which docker`.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.available_images
|
|
||||||
DockerClient.check_availability!
|
|
||||||
DockerClient.image_tags
|
|
||||||
rescue DockerClient::Error => e
|
|
||||||
raise Runner::Error::InternalServerError.new(e.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.sync_environment(_environment)
|
def self.sync_environment(_environment)
|
||||||
# There is no dedicated sync mechanism yet
|
# There is no dedicated sync mechanism yet
|
||||||
true
|
true
|
||||||
@ -38,9 +31,14 @@ class Runner::Strategy::DockerContainerPool < Runner::Strategy
|
|||||||
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished new runner request" }
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished new runner request" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(runner_id, _environment)
|
def destroy_at_management
|
||||||
super
|
url = "#{self.class.config[:pool][:location]}/docker_container_pool/destroy_container/#{container.id}"
|
||||||
@container_id = runner_id
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Destroying runner at #{url}" }
|
||||||
|
Faraday.get(url)
|
||||||
|
rescue Faraday::Error => e
|
||||||
|
raise Runner::Error::FaradayError.new("Request to DockerContainerPool failed: #{e.inspect}")
|
||||||
|
ensure
|
||||||
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished destroying runner" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy_files(files)
|
def copy_files(files)
|
||||||
@ -69,16 +67,6 @@ class Runner::Strategy::DockerContainerPool < Runner::Strategy
|
|||||||
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished copying files" }
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished copying files" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_at_management
|
|
||||||
url = "#{self.class.config[:pool][:location]}/docker_container_pool/destroy_container/#{container.id}"
|
|
||||||
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Destroying runner at #{url}" }
|
|
||||||
Faraday.get(url)
|
|
||||||
rescue Faraday::Error => e
|
|
||||||
raise Runner::Error::FaradayError.new("Request to DockerContainerPool failed: #{e.inspect}")
|
|
||||||
ensure
|
|
||||||
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished destroying runner" }
|
|
||||||
end
|
|
||||||
|
|
||||||
def attach_to_execution(command, event_loop)
|
def attach_to_execution(command, event_loop)
|
||||||
@command = command
|
@command = command
|
||||||
query_params = 'logs=0&stream=1&stderr=1&stdout=1&stdin=1'
|
query_params = 'logs=0&stream=1&stderr=1&stdout=1&stdin=1'
|
||||||
@ -99,7 +87,27 @@ class Runner::Strategy::DockerContainerPool < Runner::Strategy
|
|||||||
socket
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
def websocket_header
|
def self.available_images
|
||||||
|
DockerClient.check_availability!
|
||||||
|
DockerClient.image_tags
|
||||||
|
rescue DockerClient::Error => e
|
||||||
|
raise Runner::Error::InternalServerError.new(e.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.config
|
||||||
|
# Since the docker configuration file contains code that must be executed, we use ERB templating.
|
||||||
|
@config ||= CodeOcean::Config.new(:docker).read(erb: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.release
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pool_size
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.websocket_header
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -9,8 +9,9 @@ class Runner::Strategy::Poseidon < Runner::Strategy
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.config
|
def initialize(runner_id, _environment)
|
||||||
@config ||= CodeOcean::Config.new(:code_ocean).read[:runner_management] || {}
|
super
|
||||||
|
@allocation_id = runner_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.initialize_environment
|
def self.initialize_environment
|
||||||
@ -18,17 +19,6 @@ class Runner::Strategy::Poseidon < Runner::Strategy
|
|||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.available_images
|
|
||||||
# Images are pulled when needed for a new execution environment
|
|
||||||
# and cleaned up automatically if no longer in use.
|
|
||||||
# Hence, there is no additional image that we need to return
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.headers
|
|
||||||
@headers ||= {'Content-Type' => 'application/json', 'Poseidon-Token' => config[:token]}
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.sync_environment(environment)
|
def self.sync_environment(environment)
|
||||||
url = "#{config[:url]}/execution-environments/#{environment.id}"
|
url = "#{config[:url]}/execution-environments/#{environment.id}"
|
||||||
connection = Faraday.new nil, ssl: {ca_file: config[:ca_file]}
|
connection = Faraday.new nil, ssl: {ca_file: config[:ca_file]}
|
||||||
@ -68,38 +58,15 @@ class Runner::Strategy::Poseidon < Runner::Strategy
|
|||||||
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished new runner request" }
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished new runner request" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.handle_error(response)
|
def destroy_at_management
|
||||||
case response.status
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Destroying runner at #{runner_url}" }
|
||||||
when 400
|
connection = Faraday.new nil, ssl: {ca_file: self.class.config[:ca_file]}
|
||||||
response_body = parse response
|
response = connection.delete runner_url, nil, self.class.headers
|
||||||
raise Runner::Error::BadRequest.new(response_body[:message])
|
self.class.handle_error response unless response.status == 204
|
||||||
when 401
|
rescue Faraday::Error => e
|
||||||
raise Runner::Error::Unauthorized.new('Authentication with Poseidon failed')
|
raise Runner::Error::FaradayError.new("Request to Poseidon failed: #{e.inspect}")
|
||||||
when 404
|
ensure
|
||||||
raise Runner::Error::RunnerNotFound.new
|
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Finished destroying runner" }
|
||||||
when 500
|
|
||||||
response_body = parse response
|
|
||||||
error_code = response_body[:errorCode]
|
|
||||||
if error_code == error_nomad_overload
|
|
||||||
raise Runner::Error::NotAvailable.new("Poseidon has no runner available (#{error_code}): #{response_body[:message]}")
|
|
||||||
else
|
|
||||||
raise Runner::Error::InternalServerError.new("Poseidon sent #{response_body[:errorCode]}: #{response_body[:message]}")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise Runner::Error::UnexpectedResponse.new("Poseidon sent unexpected response status code #{response.status}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.parse(response)
|
|
||||||
JSON.parse(response.body).deep_symbolize_keys
|
|
||||||
rescue JSON::ParserError => e
|
|
||||||
# Poseidon should not send invalid json
|
|
||||||
raise Runner::Error::UnexpectedResponse.new("Error parsing response from Poseidon: #{e.message}")
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(runner_id, _environment)
|
|
||||||
super
|
|
||||||
@allocation_id = runner_id
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy_files(files)
|
def copy_files(files)
|
||||||
@ -135,24 +102,65 @@ class Runner::Strategy::Poseidon < Runner::Strategy
|
|||||||
socket
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy_at_management
|
def self.available_images
|
||||||
Rails.logger.debug { "#{Time.zone.now.getutc.inspect}: Destroying runner at #{runner_url}" }
|
# Images are pulled when needed for a new execution environment
|
||||||
connection = Faraday.new nil, ssl: {ca_file: self.class.config[:ca_file]}
|
# and cleaned up automatically if no longer in use.
|
||||||
response = connection.delete runner_url, nil, self.class.headers
|
# Hence, there is no additional image that we need to return
|
||||||
self.class.handle_error response unless response.status == 204
|
[]
|
||||||
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 destroying runner" }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def websocket_header
|
def self.config
|
||||||
|
@config ||= CodeOcean::Config.new(:code_ocean).read[:runner_management] || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.release
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.pool_size
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.websocket_header
|
||||||
{
|
{
|
||||||
tls: {root_cert_file: self.class.config[:ca_file]},
|
tls: {root_cert_file: config[:ca_file]},
|
||||||
headers: {'Poseidon-Token' => self.class.config[:token]},
|
headers: {'Poseidon-Token' => config[:token]},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.handle_error(response)
|
||||||
|
case response.status
|
||||||
|
when 400
|
||||||
|
response_body = parse response
|
||||||
|
raise Runner::Error::BadRequest.new(response_body[:message])
|
||||||
|
when 401
|
||||||
|
raise Runner::Error::Unauthorized.new('Authentication with Poseidon failed')
|
||||||
|
when 404
|
||||||
|
raise Runner::Error::RunnerNotFound.new
|
||||||
|
when 500
|
||||||
|
response_body = parse response
|
||||||
|
error_code = response_body[:errorCode]
|
||||||
|
if error_code == error_nomad_overload
|
||||||
|
raise Runner::Error::NotAvailable.new("Poseidon has no runner available (#{error_code}): #{response_body[:message]}")
|
||||||
|
else
|
||||||
|
raise Runner::Error::InternalServerError.new("Poseidon sent #{response_body[:errorCode]}: #{response_body[:message]}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise Runner::Error::UnexpectedResponse.new("Poseidon sent unexpected response status code #{response.status}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.headers
|
||||||
|
@headers ||= {'Content-Type' => 'application/json', 'Poseidon-Token' => config[:token]}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.parse(response)
|
||||||
|
JSON.parse(response.body).deep_symbolize_keys
|
||||||
|
rescue JSON::ParserError => e
|
||||||
|
# Poseidon should not send invalid json
|
||||||
|
raise Runner::Error::UnexpectedResponse.new("Error parsing response from Poseidon: #{e.message}")
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def execute_command(command)
|
def execute_command(command)
|
||||||
|
Reference in New Issue
Block a user