Working with analyzers,test-runners, and representers

This commit is contained in:
Charles Care
2019-10-14 17:59:47 +01:00
parent e837b6f5d7
commit 91ddf93357
18 changed files with 284 additions and 126 deletions

View File

@@ -49,11 +49,26 @@ class PipelineClient
params = {
action: "analyze_iteration",
track_slug: track_slug,
container_version: "v0.0.5",
container_version: "a1f5549b6391443f7a05a038fed8dfebacd3db84",
exercise_slug: exercise_slug,
solution_slug: solution_slug,
iteration_folder: iteration_folder
}
puts "MSG: #{params}"
msg = params.to_json
send_msg(msg, 10000)
end
def represent(track_slug, exercise_slug, solution_slug, iteration_folder)
params = {
action: "represent",
track_slug: track_slug,
container_version: "7dad3dd8b43c89d0ac03b5f67700c6aad52d8cf9",
exercise_slug: exercise_slug,
solution_slug: solution_slug,
iteration_folder: iteration_folder
}
puts "MSG: #{params}"
msg = params.to_json
send_msg(msg, 10000)
end
@@ -62,11 +77,12 @@ class PipelineClient
params = {
action: "test_solution",
track_slug: track_slug,
container_version: "v0.0.5",
container_version: "b6ea39ccb2dd04e0b047b25c691b17d6e6b44cfb",
exercise_slug: exercise_slug,
solution_slug: solution_slug,
iteration_folder: iteration_folder
}
puts "MSG: #{params}"
msg = params.to_json
send_msg(msg, 10000)
end

23
bin/invoke3.rb Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env ruby
require_relative "./client"
pipeline = PipelineClient.new
# return
lang = ARGV[0] || "ruby"
lang = "ruby"
r = pipeline.represent(lang, "two-fer", "soln-42", "s3://exercism-dev/iterations/fff07700-e1c3-402d-8937-823aeefb159f")
# puts r
if r["logs"]
r["logs"].each do |log_line|
puts "+ #{log_line["cmd"]}"
puts log_line["stdout"]
puts log_line["stderr"]
end
end
puts r["result"]
pipeline.close_socket

View File

@@ -12,8 +12,7 @@ require 'ffi-rzmq'
require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.setup # ready!
loader.setup
module Pipeline

View File

@@ -33,7 +33,7 @@ module Pipeline::Rpc
@backend_channels[type] = work_channel
end
@notification_port = 5556
@notification_port = 5557
@notification_socket = NotificationSocket.new(zmq_context, @notification_port)
end
@@ -98,35 +98,35 @@ module Pipeline::Rpc
end
end
def analyzer_versions
def container_versions
{
analyzer_spec: {
"ruby" => [ "v0.0.3", "v0.0.5" ]
static_analyzers: {
"ruby" => [
"a1f5549b6391443f7a05a038fed8dfebacd3db84",
"398007701db580a09f198e806e680f4cdb04b3b4",
"dc1c6c4897e63ebeb60ed53ec7423a3f6c33449d"
]
},
representers: {
"ruby" => [
"7dad3dd8b43c89d0ac03b5f67700c6aad52d8cf9"
]
},
test_runners: {
"ruby" => [
"b6ea39ccb2dd04e0b047b25c691b17d6e6b44cfb"
]
}
}
end
def emit_current_spec
analyzer_spec = analyzer_versions
m = {
action: "configure",
specs: analyzer_spec
specs: container_versions
}
set_temp_credentials(m)
# message = ["_", "", m.to_json]
puts "TODO"
puts m
notification_socket.emit_configuration(m)
# @backend_channels.each do |channel_name,v|
# m = {
# action: "configure",
# channel: channel_name,
# spec: analyzer_spec[:analyzer_spec]
# }
# puts v
# puts m
# end
end
def respond_with_worker_config(req)
@@ -137,7 +137,7 @@ module Pipeline::Rpc
end
channel = channel.to_sym
analyzer_spec = {}
analyzer_spec["specs"] = analyzer_versions
analyzer_spec["specs"] = container_versions
analyzer_spec[:channel] = {
channel: channel,
workqueue_address: "tcp://#{@public_hostname}:#{@work_channel_ports[channel]}",

View File

@@ -9,6 +9,11 @@ module Pipeline::Rpc::Worker
@return_address = return_address
end
def setup(track_slug, version, exercise_slug, solution_slug)
track_dir = environment.track_dir(track_slug, version)
Pipeline::Runtime::AnalysisRun.new(track_dir, exercise_slug, solution_slug)
end
def invoke
s3 = Aws::S3::Client.new(
credentials: parse_credentials(request["context"]),
@@ -26,7 +31,7 @@ module Pipeline::Rpc::Worker
}
end
analysis_run = environment.new_invocation(language_slug, container_version, exercise_slug, solution_slug)
analysis_run = setup(language_slug, container_version, exercise_slug, solution_slug)
analysis_run.prepare_iteration do |iteration_folder|
location_uri = URI(location)
bucket = location_uri.host

View File

@@ -2,8 +2,13 @@ module Pipeline::Rpc::Worker
class ConfigureAction < WorkerAction
def initialize(channel, request)
@channel = channel
@request = request
end
def invoke
spec = request["specs"]["analyzer_spec"]
spec = request["specs"][@channel]
credentials = parse_credentials(request)
raise "No spec received" if spec.nil?
spec.each do |language_slug, versions|
@@ -13,7 +18,7 @@ module Pipeline::Rpc::Worker
puts "Already installed #{language_slug}:#{version}"
else
puts "Installed #{language_slug}"
environment.release_analyzer(language_slug, version, credentials)
environment.release(@channel, language_slug, version, credentials)
end
end
end

View File

@@ -34,9 +34,8 @@ module Pipeline::Rpc::Worker
environment.prepare
action = Pipeline::Rpc::Worker::ConfigureAction.new
action = Pipeline::Rpc::Worker::ConfigureAction.new(@channel, msg)
action.environment = environment
action.request = msg
action.invoke
response_address = msg["channel"]["response_address"]
@@ -53,7 +52,7 @@ module Pipeline::Rpc::Worker
setup
@incoming_wrapper = Pipeline::Rpc::Worker::WorkSocketWrapper.new(incoming)
@noificationincoming_wrapper = Pipeline::Rpc::Worker::NotificationSocketWrapper.new(@notifications)
@noificationincoming_wrapper = Pipeline::Rpc::Worker::NotificationSocketWrapper.new(@notifications, @channel)
@poller = Pipeline::Rpc::ChannelPoller.new
@poller.register(@incoming_wrapper)

View File

@@ -1,10 +1,11 @@
module Pipeline::Rpc::Worker
class NotificationSocketWrapper
attr_reader :socket
attr_reader :socket, :channel
def initialize(socket)
def initialize(socket, channel)
@socket = socket
@channel = channel
end
def recv
@@ -15,14 +16,10 @@ module Pipeline::Rpc::Worker
request = JSON.parse(msg)
action = request["action"]
puts "HERE #{action}"
if action == "configure"
a = Pipeline::Rpc::Worker::ConfigureAction.new
a = Pipeline::Rpc::Worker::ConfigureAction.new(channel, request)
a.request = request
a
elsif action == "analyze_iteration" || action == "test_solution"
a = Pipeline::Rpc::Worker::AnalyzeAction.new(request, return_address)
a
else
puts "HERE ELSE: #{request}"
end

View File

@@ -0,0 +1,15 @@
module Pipeline::Rpc::Worker
class RepresentAction < AnalyzeAction
def initialize(request, return_address)
super(request, return_address)
end
def setup(track_slug, version, exercise_slug, solution_slug)
track_dir = environment.track_dir(track_slug, version)
Pipeline::Runtime::RepresentRun.new(track_dir, exercise_slug, solution_slug)
end
end
end

View File

@@ -0,0 +1,15 @@
module Pipeline::Rpc::Worker
class TestRunnerAction < AnalyzeAction
def initialize(request, return_address)
super(request, return_address)
end
def setup(track_slug, version, exercise_slug, solution_slug)
track_dir = environment.track_dir(track_slug, version)
Pipeline::Runtime::TestRun.new(track_dir, exercise_slug, solution_slug)
end
end
end

View File

@@ -18,12 +18,12 @@ module Pipeline::Rpc::Worker
request = JSON.parse(raw_request)
action = request["action"]
if action == "configure"
ConfigureAction.new
elsif action == "analyze_iteration" || action == "test_solution"
a = AnalyzeAction.new(request, return_address)
a.request = request
a
if action == "analyze_iteration"
AnalyzeAction.new(request, return_address)
elsif action == "represent"
RepresentAction.new(request, return_address)
elsif action == "test_solution"
TestRunnerAction.new(request, return_address)
else
puts "HERE ELSE: #{request}"
end

View File

@@ -1,82 +1,17 @@
module Pipeline::Runtime
class AnalysisRun
class AnalysisRun < ContainerRun
attr_reader :track_dir, :exercise_slug, :runs_dir, :solution_dir,
:iteration_folder, :tmp_folder, :current_dir, :img,
:runc
def initialize(track_dir, exercise_slug, solution_slug)
@track_dir = track_dir
@exercise_slug = exercise_slug
@runs_dir = "#{track_dir}/runs"
@current_dir = "#{track_dir}/current"
@solution_dir = "#{runs_dir}/iteration_#{Time.now.to_i}-#{solution_slug}-#{SecureRandom.hex}"
@iteration_folder = "#{solution_dir}/iteration"
@tmp_folder = "#{solution_dir}/tmp"
@logs = Pipeline::Util::LogCollector.new
@img = Pipeline::Util::ImgWrapper.new(@logs)
@runc = Pipeline::Util::RuncWrapper.new(@logs)
end
def prepare_iteration
FileUtils.mkdir_p iteration_folder
FileUtils.mkdir_p tmp_folder
configurator.setup_for_terminal_access
File.write("#{solution_dir}/terminal_config.json", configurator.build.to_json)
yield iteration_folder
end
def analyze!
container_driver = Pipeline::Util::ContainerDriver.new(runc, img, configurator, solution_dir)
@result = container_driver.run_analyzer_for(exercise_slug)
{
exercise_slug: exercise_slug,
solution_dir: solution_dir,
rootfs_source: rootfs_source,
result: result,
invocation: @result.report,
logs: @logs.inspect
}
def args
["bin/analyze.sh", exercise_slug, "/mnt/exercism-iteration/"]
end
def result
File.read("#{iteration_folder}/analysis.json")
end
def success?
@result.success?
def working_directory
"/opt/analyzer"
end
def exit_status
@result.exit_status
end
def stdout
@result.stdout
end
def stderr
@result.stderr
end
private
def configurator
@configurator ||= begin
configurator = Pipeline::Util::RuncConfigurator.new
configurator.seed_from_env
configurator.rootfs = rootfs_source
configurator
end
end
def rootfs_source
@rootfs_source ||= begin
release_folder = File.readlink(current_dir)
"#{release_folder}/rootfs"
end
end
end
end

View File

@@ -0,0 +1,91 @@
module Pipeline::Runtime
class ContainerRun
attr_reader :track_dir, :exercise_slug, :runs_dir, :solution_dir,
:iteration_folder, :tmp_folder, :current_dir, :img,
:runc
def initialize(track_dir, exercise_slug, solution_slug)
@track_dir = track_dir
@exercise_slug = exercise_slug
@runs_dir = "#{track_dir}/runs"
@current_dir = "#{track_dir}/current"
@solution_dir = "#{runs_dir}/iteration_#{Time.now.to_i}-#{solution_slug}-#{SecureRandom.hex}"
@iteration_folder = "#{solution_dir}/iteration"
@tmp_folder = "#{solution_dir}/tmp"
@logs = Pipeline::Util::LogCollector.new
@img = Pipeline::Util::ImgWrapper.new(@logs)
@runc = Pipeline::Util::RuncWrapper.new(@logs)
end
def prepare_iteration
FileUtils.mkdir_p iteration_folder
FileUtils.mkdir_p tmp_folder
configurator.setup_for_terminal_access
File.write("#{solution_dir}/terminal_config.json", configurator.build.to_json)
yield iteration_folder
end
def analyze!
container_driver = Pipeline::Util::ContainerDriver.new(runc, img, configurator, solution_dir)
@result = container_driver.invoke(working_directory, args)
puts @logs.inspect
{
exercise_slug: exercise_slug,
solution_dir: solution_dir,
rootfs_source: rootfs_source,
result: result,
invocation: @result.report,
logs: @logs.inspect
}
end
def working_directory
raise "Working directory was not defined"
end
def args
raise "args were not defined"
end
def result
raise "No result handler defined"
end
def success?
@result.success?
end
def exit_status
@result.exit_status
end
def stdout
@result.stdout
end
def stderr
@result.stderr
end
private
def configurator
@configurator ||= begin
configurator = Pipeline::Util::RuncConfigurator.new
configurator.seed_from_env
configurator.rootfs = rootfs_source
configurator
end
end
def rootfs_source
@rootfs_source ||= begin
release_folder = File.readlink(current_dir)
"#{release_folder}/rootfs"
end
end
end
end

View File

@@ -0,0 +1,17 @@
module Pipeline::Runtime
class RepresentRun < ContainerRun
def args
["bin/generate.sh", exercise_slug, "/mnt/exercism-iteration/"]
end
def result
File.read("#{iteration_folder}/representation.txt")
end
def working_directory
"/opt/representer"
end
end
end

View File

@@ -17,18 +17,24 @@ module Pipeline::Runtime
File.exist? current_dir
end
def available?(track_slug, version)
puts "-- CHECK #{version} -----"
puts container_repo.list_images
puts "-------------------------"
true
def release(channel, language_slug, version, credentials)
container_slug = case channel
when "static_analyzers"
"#{language_slug}-analyzer"
when "test_runners"
"#{language_slug}-test-runner"
when "representers"
"#{language_slug}-representer"
# when "static_analyzers"
# container_slug = "#{language_slug}-analyzer"
else
raise "Unknown channel: #{channel}"
end
container_repo = Pipeline::ContainerRepo.new(container_slug, credentials)
release_container(language_slug, version, container_repo)
end
def container_repo
@container_repo ||= Pipeline::ContainerRepo.new("#{track_slug}-analyzer-dev", credentials)
end
def release_analyzer(track_slug, version, credentials)
def release_container(track_slug, version, container_repo)
track_dir = "#{env_base}/#{track_slug}/#{version}"
release_dir = "#{track_dir}/releases/#{Time.now.to_i}_release"
current_dir = "#{track_dir}/current"
@@ -42,7 +48,6 @@ module Pipeline::Runtime
configurator.seed_from_env
container_driver = Pipeline::Util::ContainerDriver.new(runc, img, configurator, release_dir)
user,password = container_repo.create_login_token
img.reset_hub_login
img.login("AWS", password, container_repo.repository_url)
@@ -65,6 +70,11 @@ module Pipeline::Runtime
FileUtils.symlink(release_dir, current_dir, force: true)
end
def track_dir(track_slug, version)
container_slug = "#{track_slug}/#{version}"
"#{env_base}/#{container_slug}"
end
def new_invocation(track_slug, version, exercise_slug, solution_slug)
container_slug = "#{track_slug}/#{version}"
puts "AnalysisRun: #{container_slug} #{exercise_slug} #{solution_slug}"

View File

@@ -0,0 +1,17 @@
module Pipeline::Runtime
class TestRun < ContainerRun
def args
["bin/run.sh", exercise_slug, "/mnt/exercism-iteration/", "/mnt/exercism-iteration/"]
end
def result
File.read("#{iteration_folder}/results.json")
end
def working_directory
"/opt/test-runner"
end
end
end

View File

@@ -45,6 +45,13 @@ module Pipeline::Util
run_analyzer
end
def invoke(container_work_dir, args)
configurator.setup_invocation_args(container_work_dir, args)
File.write("#{workdir}/invocation_config.json", configurator.build.to_json)
FileUtils.symlink("#{workdir}/invocation_config.json", "#{workdir}/config.json", force: true)
run_analyzer
end
end
end

View File

@@ -1,9 +1,10 @@
module Pipeline::Util
class RuncConfigurator
attr_accessor :uid_id, :gid_id, :invocation_args, :interactive, :rootfs
attr_accessor :uid_id, :gid_id, :invocation_args, :interactive, :rootfs, :working_directory
def initialize
@rootfs = "./rootfs"
@working_directory = "/opt/analyzer"
end
def seed_from_env
@@ -18,6 +19,12 @@ module Pipeline::Util
@invocation_args = ["bin/analyze.sh", track_slug, "/mnt/exercism-iteration/"]
end
def setup_invocation_args(working_directory, args)
@interactive = false
@working_directory = working_directory
@invocation_args = args
end
def setup_for_terminal_access
@interactive = true
@invocation_args = ["/bin/sh"]
@@ -42,7 +49,7 @@ module Pipeline::Util
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/opt/analyzer",
"cwd": "#{working_directory}",
"rlimits": [
{
"type": "RLIMIT_NOFILE",