Refactor to use new status codes
This commit is contained in:
@@ -1,6 +1,28 @@
|
|||||||
module Pipeline::Rpc
|
module Pipeline::Rpc
|
||||||
class FrontEndRequest
|
class FrontEndRequest
|
||||||
|
|
||||||
|
DEFAULT_RESPONSES = {
|
||||||
|
|
||||||
|
500 => "Internal platform Error",
|
||||||
|
501 => "Unrecognised_action",
|
||||||
|
502 => "Malformed request",
|
||||||
|
503 => "No worker available",
|
||||||
|
504 => "Request timed out while waiting for response from worker",
|
||||||
|
|
||||||
|
510 => "Worker error",
|
||||||
|
511 => "Container version is not available",
|
||||||
|
512 => "Failure in container setup",
|
||||||
|
513 => "Failuter in container invocation",
|
||||||
|
514 => "Output missing or malformed",
|
||||||
|
|
||||||
|
400 => "Bad input",
|
||||||
|
401 => "Forced exit. Container ran too long",
|
||||||
|
402 => "Forced exit. Container used too much IO",
|
||||||
|
403 => "Forced exit. Container was terminated early.",
|
||||||
|
|
||||||
|
200 => "OK"
|
||||||
|
}
|
||||||
|
|
||||||
def self.recv(socket)
|
def self.recv(socket)
|
||||||
msg = []
|
msg = []
|
||||||
socket.recv_strings(msg)
|
socket.recv_strings(msg)
|
||||||
@@ -14,32 +36,19 @@ module Pipeline::Rpc
|
|||||||
@raw_msg = msg_strings[2]
|
@raw_msg = msg_strings[2]
|
||||||
@socket = socket
|
@socket = socket
|
||||||
@start = current_timestamp
|
@start = current_timestamp
|
||||||
|
@params_to_check = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_error(err, status_code=999)
|
def send_error(msg, status_code, detail={})
|
||||||
msg = {
|
status_code ||= 500
|
||||||
status: {
|
detail ||= {}
|
||||||
ok: false,
|
detail[:error] = msg
|
||||||
status_code: status_code
|
detail[:failed_request] = parsed_msg
|
||||||
},
|
send_reply(status_code, detail)
|
||||||
error: err,
|
|
||||||
failed_request: parsed_msg
|
|
||||||
}
|
|
||||||
send_reply(msg)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_result(result, status_code=0)
|
def send_result(result)
|
||||||
msg = {
|
send_reply(200, result)
|
||||||
status: {
|
|
||||||
ok: true,
|
|
||||||
status_code: status_code
|
|
||||||
},
|
|
||||||
result: result,
|
|
||||||
timing: {
|
|
||||||
start_time: @start.to_i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
send_reply(msg)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_timestamp
|
def current_timestamp
|
||||||
@@ -51,32 +60,99 @@ module Pipeline::Rpc
|
|||||||
5
|
5
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_reply(msg)
|
|
||||||
@end = current_timestamp
|
|
||||||
@duration_milliseconds = @end - @start
|
|
||||||
msg[:timing] = {
|
|
||||||
start_time: @start.to_i,
|
|
||||||
end_time: @end.to_i,
|
|
||||||
duration_milliseconds: @duration_milliseconds.to_i
|
|
||||||
}
|
|
||||||
reply = [raw_address, "", msg.to_json]
|
|
||||||
@socket.send_strings(reply)
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle
|
def handle
|
||||||
begin
|
begin
|
||||||
@parsed_msg = JSON.parse(raw_msg)
|
@parsed_msg = JSON.parse(raw_msg)
|
||||||
rescue JSON::ParserError => e
|
rescue JSON::ParserError => e
|
||||||
req.send_error({ status: :parse_error })
|
puts e.message
|
||||||
|
detail = {
|
||||||
|
incoming: raw_msg
|
||||||
|
}
|
||||||
|
send_error("Could not parse message", 502, detail)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
action = @parsed_msg["action"]
|
action = @parsed_msg["action"]
|
||||||
if action.nil?
|
if action.nil?
|
||||||
req.send_error({ status: :no_action })
|
send_error("No action specified", 502)
|
||||||
else
|
else
|
||||||
yield(action)
|
begin
|
||||||
|
yield(action)
|
||||||
|
rescue => e
|
||||||
|
puts e.message
|
||||||
|
detail = {
|
||||||
|
message: e.message,
|
||||||
|
trace: e.backtrace
|
||||||
|
}
|
||||||
|
send_error("Unhandled error", 500, detail)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_param(param_name)
|
||||||
|
@params_to_check << param_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def params_missing?
|
||||||
|
! missing_params.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def missing_params
|
||||||
|
@missing_params ||= begin
|
||||||
|
missing = []
|
||||||
|
@params_to_check.each do |param|
|
||||||
|
missing << param unless parsed_msg.include?(param)
|
||||||
|
end
|
||||||
|
missing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_context!(context_to_merge)
|
||||||
|
context.merge!(context_to_merge)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def send_reply(status_code, payload)
|
||||||
|
msg = assemble_response(status_code, payload)
|
||||||
|
reply = [raw_address, "", msg.to_json]
|
||||||
|
@socket.send_strings(reply)
|
||||||
|
end
|
||||||
|
|
||||||
|
def assemble_response(status_code, payload)
|
||||||
|
@end = current_timestamp
|
||||||
|
@duration_milliseconds = @end - @start
|
||||||
|
context[:timing] = {
|
||||||
|
start_time: @start.to_i,
|
||||||
|
end_time: @end.to_i,
|
||||||
|
duration_milliseconds: @duration_milliseconds.to_i
|
||||||
|
}
|
||||||
|
msg = {
|
||||||
|
status: status_for(status_code),
|
||||||
|
context: context
|
||||||
|
}
|
||||||
|
if status_code == 200
|
||||||
|
msg[:response] = payload
|
||||||
|
else
|
||||||
|
msg[:context][:error_detail] = payload
|
||||||
|
msg[:status][:error] = payload[:error] unless payload.nil?
|
||||||
|
end
|
||||||
|
msg
|
||||||
|
end
|
||||||
|
|
||||||
|
def context
|
||||||
|
@context ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_for(code)
|
||||||
|
textual_message = DEFAULT_RESPONSES[code]
|
||||||
|
if textual_message.nil?
|
||||||
|
group_code = (code.to_i / 100) * 100
|
||||||
|
textual_message = DEFAULT_RESPONSES[group_code]
|
||||||
|
end
|
||||||
|
{
|
||||||
|
status_code: code,
|
||||||
|
message: textual_message
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ module Pipeline::Rpc
|
|||||||
@in_flight[req.raw_address] = {timeout: timeout_at, req: req}
|
@in_flight[req.raw_address] = {timeout: timeout_at, req: req}
|
||||||
end
|
end
|
||||||
|
|
||||||
def forward_response(msg)
|
def forward_as_error(msg)
|
||||||
addr = msg.binary_return_address
|
addr = msg.binary_return_address
|
||||||
entry = @in_flight[addr]
|
entry = @in_flight[addr]
|
||||||
if entry.nil?
|
if entry.nil?
|
||||||
@@ -19,8 +19,29 @@ module Pipeline::Rpc
|
|||||||
else
|
else
|
||||||
req = entry[:req]
|
req = entry[:req]
|
||||||
resp = msg.parsed_msg
|
resp = msg.parsed_msg
|
||||||
|
resp.delete("msg_type")
|
||||||
resp.delete("return_address")
|
resp.delete("return_address")
|
||||||
req.send_result(msg.parsed_msg)
|
worker_error_code = resp.delete("worker_error_code") || 510
|
||||||
|
req.send_error("Error from worker", worker_error_code, resp)
|
||||||
|
unregister(addr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def forward_as_response(msg)
|
||||||
|
addr = msg.binary_return_address
|
||||||
|
entry = @in_flight[addr]
|
||||||
|
if entry.nil?
|
||||||
|
puts "dropping response"
|
||||||
|
else
|
||||||
|
req = entry[:req]
|
||||||
|
resp = msg.parsed_msg
|
||||||
|
resp.delete("msg_type")
|
||||||
|
resp.delete("return_address")
|
||||||
|
container_response = resp.delete("result")
|
||||||
|
puts "keys: #{resp.keys}"
|
||||||
|
puts "Here: #{resp}"
|
||||||
|
req.merge_context!(resp)
|
||||||
|
req.send_result(container_response)
|
||||||
unregister(addr)
|
unregister(addr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -33,7 +54,7 @@ module Pipeline::Rpc
|
|||||||
timed_out << entry[:req] if expiry < now
|
timed_out << entry[:req] if expiry < now
|
||||||
end
|
end
|
||||||
timed_out.each do |req|
|
timed_out.each do |req|
|
||||||
req.send_error({status: :timeout})
|
req.send_error("Timed out request", 504)
|
||||||
puts "Timing out #{req}"
|
puts "Timing out #{req}"
|
||||||
unregister(req.raw_address)
|
unregister(req.raw_address)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -81,9 +81,9 @@ module Pipeline::Rpc
|
|||||||
|
|
||||||
def on_service_response(msg)
|
def on_service_response(msg)
|
||||||
if msg.type == "response"
|
if msg.type == "response"
|
||||||
@in_flight_requests.forward_response(msg)
|
@in_flight_requests.forward_as_response(msg)
|
||||||
elsif msg.type == "error_response"
|
elsif msg.type == "error_response"
|
||||||
@in_flight_requests.forward_response(msg)
|
@in_flight_requests.forward_as_error(msg)
|
||||||
elsif msg.type == "heartbeat"
|
elsif msg.type == "heartbeat"
|
||||||
@in_flight_requests.flush_expired_requests
|
@in_flight_requests.flush_expired_requests
|
||||||
emit_current_spec
|
emit_current_spec
|
||||||
@@ -97,10 +97,25 @@ module Pipeline::Rpc
|
|||||||
if action == "configure_worker"
|
if action == "configure_worker"
|
||||||
respond_with_worker_config(req)
|
respond_with_worker_config(req)
|
||||||
elsif action == "analyze_iteration"
|
elsif action == "analyze_iteration"
|
||||||
|
# TODO check mandatory args
|
||||||
|
req.ensure_param("id")
|
||||||
|
req.ensure_param("track_slug")
|
||||||
|
req.ensure_param("exercise_slug")
|
||||||
|
req.ensure_param("s3_uri")
|
||||||
|
req.ensure_param("container_version")
|
||||||
handle_with_worker(:static_analyzers, req)
|
handle_with_worker(:static_analyzers, req)
|
||||||
elsif action == "test_solution"
|
elsif action == "test_solution"
|
||||||
|
req.ensure_param("id")
|
||||||
|
req.ensure_param("track_slug")
|
||||||
|
req.ensure_param("exercise_slug")
|
||||||
|
req.ensure_param("s3_uri")
|
||||||
|
req.ensure_param("container_version")
|
||||||
handle_with_worker(:test_runners, req)
|
handle_with_worker(:test_runners, req)
|
||||||
elsif action == "represent"
|
elsif action == "represent"
|
||||||
|
# TODO check mandatory args
|
||||||
|
req.ensure_param("id")
|
||||||
|
req.ensure_param("track_slug")
|
||||||
|
req.ensure_param("container_version")
|
||||||
handle_with_worker(:representers, req)
|
handle_with_worker(:representers, req)
|
||||||
elsif action == "build_container"
|
elsif action == "build_container"
|
||||||
handle_with_worker(:builders, req)
|
handle_with_worker(:builders, req)
|
||||||
@@ -126,7 +141,7 @@ module Pipeline::Rpc
|
|||||||
images = container_repo.images_info
|
images = container_repo.images_info
|
||||||
req.send_result({ list_images: images })
|
req.send_result({ list_images: images })
|
||||||
else
|
else
|
||||||
req.send_error({ status: :unrecognised_action })
|
req.send_error("Action <#{action}> unrecognised", 501)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -139,12 +154,20 @@ module Pipeline::Rpc
|
|||||||
end
|
end
|
||||||
|
|
||||||
def handle_with_worker(worker_class, req)
|
def handle_with_worker(worker_class, req)
|
||||||
|
if req.params_missing?
|
||||||
|
puts "MISSING"
|
||||||
|
error = {
|
||||||
|
missing_params: req.missing_params
|
||||||
|
}
|
||||||
|
req.send_error("Missing mandatory paraneters", 502, error)
|
||||||
|
return
|
||||||
|
end
|
||||||
channel = select_channel(worker_class)
|
channel = select_channel(worker_class)
|
||||||
if channel.nil?
|
if channel.nil?
|
||||||
req.send_error({ status: :worker_class_unknown })
|
req.send_error("worker_class <#{worker_class}> unrecognised", 502)
|
||||||
else
|
return
|
||||||
select_backend_and_forward(req, channel)
|
|
||||||
end
|
end
|
||||||
|
select_backend_and_forward(req, channel)
|
||||||
end
|
end
|
||||||
|
|
||||||
def select_channel(worker_class)
|
def select_channel(worker_class)
|
||||||
@@ -162,7 +185,7 @@ module Pipeline::Rpc
|
|||||||
if backend && backend.worker_available?
|
if backend && backend.worker_available?
|
||||||
forward(backend, req)
|
forward(backend, req)
|
||||||
else
|
else
|
||||||
req.send_error({ status: :worker_unavailable })
|
req.send_error("No workers available for <#{track_slug}>", 503)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ module Pipeline::Rpc::Worker
|
|||||||
msg = "Container #{track_slug}:#{container_version} isn't available"
|
msg = "Container #{track_slug}:#{container_version} isn't available"
|
||||||
log msg
|
log msg
|
||||||
@error = {
|
@error = {
|
||||||
status_code: 404,
|
worker_error_code: 511,
|
||||||
error: msg
|
error: msg
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@@ -53,7 +53,7 @@ module Pipeline::Rpc::Worker
|
|||||||
msg = "Failure accessing environment (during container check)"
|
msg = "Failure accessing environment (during container check)"
|
||||||
log msg
|
log msg
|
||||||
@error = {
|
@error = {
|
||||||
status_code: 500,
|
worker_error_code: 512,
|
||||||
error: msg,
|
error: msg,
|
||||||
detail: e
|
detail: e
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@ module Pipeline::Rpc::Worker
|
|||||||
msg = "Failure setting up job"
|
msg = "Failure setting up job"
|
||||||
log msg
|
log msg
|
||||||
@error = {
|
@error = {
|
||||||
status_code: 500,
|
worker_error_code: 512,
|
||||||
error: msg,
|
error: msg,
|
||||||
detail: e
|
detail: e
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ module Pipeline::Rpc::Worker
|
|||||||
msg = "Failure preparing input"
|
msg = "Failure preparing input"
|
||||||
log msg
|
log msg
|
||||||
@error = {
|
@error = {
|
||||||
status_code: 500,
|
worker_error_code: 512,
|
||||||
error: msg,
|
error: msg,
|
||||||
detail: e
|
detail: e
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,7 @@ module Pipeline::Rpc::Worker
|
|||||||
msg = "Error from container"
|
msg = "Error from container"
|
||||||
log msg
|
log msg
|
||||||
@error = {
|
@error = {
|
||||||
status_code: 500,
|
worker_error_code: 513,
|
||||||
error: msg,
|
error: msg,
|
||||||
detail: e
|
detail: e
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,15 @@ module Pipeline::Rpc::Worker
|
|||||||
@setup.send_string(request.to_json)
|
@setup.send_string(request.to_json)
|
||||||
msg = ""
|
msg = ""
|
||||||
@setup.recv_string(msg)
|
@setup.recv_string(msg)
|
||||||
@bootstrap = JSON.parse(msg)["result"]
|
parsed_msg = JSON.parse(msg)
|
||||||
|
puts parsed_msg
|
||||||
|
status_code = parsed_msg["status"]["status_code"]
|
||||||
|
if status_code != 200
|
||||||
|
puts "Error when configuring"
|
||||||
|
puts "Recieved: #{msg}"
|
||||||
|
raise "Got status #{status_code} when trying to configure"
|
||||||
|
end
|
||||||
|
@bootstrap = parsed_msg["response"]
|
||||||
puts "Bootstrap with #{JSON.pretty_generate(@bootstrap)}"
|
puts "Bootstrap with #{JSON.pretty_generate(@bootstrap)}"
|
||||||
@setup.close
|
@setup.close
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ module Pipeline::Runtime
|
|||||||
end
|
end
|
||||||
|
|
||||||
def result
|
def result
|
||||||
File.read("#{iteration_folder}/results.json")
|
raw_results = File.read("#{iteration_folder}/results.json")
|
||||||
|
JSON.parse(raw_results)
|
||||||
end
|
end
|
||||||
|
|
||||||
def working_directory
|
def working_directory
|
||||||
|
|||||||
100
scripts/client/scratch.rb
Normal file
100
scripts/client/scratch.rb
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
require 'ffi-rzmq'
|
||||||
|
require 'pp'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
@context = ZMQ::Context.new(1)
|
||||||
|
|
||||||
|
@socket = @context.socket(ZMQ::REQ)
|
||||||
|
@socket.setsockopt(ZMQ::LINGER, 0)
|
||||||
|
@socket.connect("tcp://localhost:5555")
|
||||||
|
|
||||||
|
def exchange(msg)
|
||||||
|
@socket.setsockopt(ZMQ::RCVTIMEO, 10000)
|
||||||
|
@socket.send_string(msg)
|
||||||
|
return_code = @socket.recv_string(response = "")
|
||||||
|
|
||||||
|
# LOCAL error condition 1, no network exchange
|
||||||
|
puts "network timeout" if return_code == -1
|
||||||
|
|
||||||
|
# LOCAL error 2, malformed response
|
||||||
|
response = JSON.parse(response)
|
||||||
|
|
||||||
|
puts "----------------------"
|
||||||
|
pp response["status"]
|
||||||
|
puts "----------------------"
|
||||||
|
|
||||||
|
puts "----------------------"
|
||||||
|
pp response["response"]
|
||||||
|
puts "----------------------"
|
||||||
|
|
||||||
|
puts "----------------------"
|
||||||
|
pp response["context"]
|
||||||
|
puts "----------------------"
|
||||||
|
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
result = exchange("foo") # - 502
|
||||||
|
raise "error" unless result["status"]["status_code"] == 502
|
||||||
|
|
||||||
|
result = exchange("{}") # - 502
|
||||||
|
raise "error" unless result["status"]["status_code"] == 502
|
||||||
|
|
||||||
|
result = exchange("{ \"action\": \"beep\"}") # - 501
|
||||||
|
raise "error" unless result["status"]["status_code"] == 501
|
||||||
|
|
||||||
|
result = exchange("{ \"action\": \"test_solution\"}") # - 502 (no track slug)
|
||||||
|
raise "error" unless result["status"]["status_code"] == 502
|
||||||
|
|
||||||
|
result = exchange("{ \"action\": \"represent\", \"track_slug\": \"parsnip\"}") # - 502 (no container_version slug)
|
||||||
|
raise "error" unless result["status"]["status_code"] == 502
|
||||||
|
|
||||||
|
result = exchange("{ \"action\": \"represent\", \"track_slug\": \"parsnip\", \"container_version\": \"1234\", \"id\": \"_myid\"}") # - 503 (no worker()
|
||||||
|
raise "error" unless result["status"]["status_code"] == 503
|
||||||
|
|
||||||
|
## 504
|
||||||
|
|
||||||
|
|
||||||
|
msg = {
|
||||||
|
action: "test_solution",
|
||||||
|
track_slug: "parsnip",
|
||||||
|
container_version: "1234",
|
||||||
|
id: "_myid",
|
||||||
|
exercise_slug: "xx",
|
||||||
|
s3_uri: "xx"
|
||||||
|
}
|
||||||
|
|
||||||
|
result = exchange(msg.to_json)
|
||||||
|
raise "error" unless result["status"]["status_code"] == 511
|
||||||
|
|
||||||
|
|
||||||
|
msg = {
|
||||||
|
action: "test_solution",
|
||||||
|
track_slug: "ruby",
|
||||||
|
container_version: "git-b6ea39ccb2dd04e0b047b25c691b17d6e6b44cfb",
|
||||||
|
id: "_myid",
|
||||||
|
exercise_slug: "xx",
|
||||||
|
s3_uri: "xx"
|
||||||
|
}
|
||||||
|
|
||||||
|
result = exchange(msg.to_json)
|
||||||
|
raise "error" unless result["status"]["status_code"] == 512
|
||||||
|
#
|
||||||
|
# pp result
|
||||||
|
# exit 1
|
||||||
|
|
||||||
|
msg = {
|
||||||
|
action: "test_solution",
|
||||||
|
track_slug: "ruby",
|
||||||
|
container_version: "git-b6ea39ccb2dd04e0b047b25c691b17d6e6b44cfb",
|
||||||
|
id: "_myid",
|
||||||
|
exercise_slug: "two-fer",
|
||||||
|
s3_uri: "s3://exercism-submissions/production/submissions/96"
|
||||||
|
}
|
||||||
|
|
||||||
|
result = exchange(msg.to_json)
|
||||||
|
raise "error" unless result["status"]["status_code"] == 200
|
||||||
|
|
||||||
|
pp result
|
||||||
|
|
||||||
|
puts "done"
|
||||||
Reference in New Issue
Block a user