Skip to content

Commit 0b6d594

Browse files
committedJun 3, 2015
latest work on rewrite
1 parent 1cead72 commit 0b6d594

18 files changed

+874
-465
lines changed
 

‎.idea/kymera.iml

+13-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.idea/workspace.xml

+176-209
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Gemfile

+7-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ source 'http://rubygems.org'
22

33
# Specify your gem's dependencies in kymera.gemspec
44
gem 'cucumber'
5-
gem 'ffi-rzmq'
5+
gem 'ffi-rzmq', '2.0.1'
66
gem 'chronic'
7-
gem 'mongo', '1.10.2'
8-
gem 'bson', '1.10.2'
9-
gem 'bson_ext', '1.10.2'
7+
gem 'mongo', '1.11.1'
8+
gem 'bson', '1.11.1'
9+
gem 'bson_ext', '1.11.1'
10+
# gem 'mongo', '2.0.4'
11+
# gem 'bson'
12+
# gem 'bson_ext'
1013
gem 'rspec'
1114
gem 'pry', :group => :development
1215
gemspec

‎kymera.gemspec

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ Gem::Specification.new do |spec|
2121
spec.add_development_dependency "bundler", "~> 1.3"
2222
spec.add_development_dependency "rake"
2323
spec.add_dependency 'cucumber'
24-
spec.add_dependency 'ffi-rzmq'
24+
spec.add_dependency 'ffi-rzmq', '2.0.1'
2525
spec.add_dependency 'json'
26-
spec.add_dependency 'mongo', '1.10.2'
27-
spec.add_dependency 'bson', '1.10.2'
28-
spec.add_dependency 'bson_ext', '~> 1.10.0'
26+
spec.add_dependency 'mongo', '1.11.1'
27+
spec.add_dependency 'bson', '1.11.1'
28+
spec.add_dependency 'bson_ext', '1.11.1'
2929
spec.add_dependency 'chronic'
3030

3131
end

‎lib/kymera.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# require_relative 'kymera/cucumber/cucumber_test_runner'
44
require_relative 'kymera/v2/cucumber/cucumber_test_runner'
55
# require_relative 'kymera/worker'
6-
require_relative 'kymera/broker'
6+
# require_relative 'kymera/broker'
77
require_relative 'kymera/client'
88
# require_relative 'kymera/cucumber/test_parser'
99
require_relative 'kymera/v2/cucumber/test_parser'
@@ -19,6 +19,7 @@
1919
require_relative '../lib/kymera/v2/worker_v2'
2020
require_relative '../lib/kymera/v2/registry'
2121
require_relative '../lib/kymera/v2/node'
22+
require_relative '../lib/kymera/v2/broker'
2223

2324
module Kymera
2425

‎lib/kymera/broker.rb

+189-189
Original file line numberDiff line numberDiff line change
@@ -1,189 +1,189 @@
1-
require_relative 'szmq/szmq'
2-
require 'json'
3-
4-
module Kymera
5-
6-
class Broker
7-
8-
def initialize
9-
config = Kymera::Config.new
10-
@zmq = Kymera::SZMQ.new
11-
Thread.abort_on_exception = true
12-
#This will change once I get the worker registry up and running. When that is up, there will be one connection spawned for
13-
#each of the workers that are connected. Edit - Thats actually not sustainable. The number of workers should be able to change
14-
#dynamically without impact to the broker. An inventory check should be made at the start of every new test run. There should also
15-
# be some logic to handle the disconnection of workers in the middle of a test run.
16-
@num_of_connections = config.broker["number_of_connections"]
17-
#This socket is for getting tests from the client
18-
@client_address = "tcp://*:#{config.broker["client_listening_port"]}"
19-
@internal_address = "tcp://*:#{config.broker["internal_worker_port"]}"
20-
@worker_address = "tcp://*:#{config.broker["worker_listening_port"]}"
21-
@test_socket = @zmq.socket(@client_address, 'pull')
22-
@test_socket.bind
23-
@front_end = @zmq.socket(@internal_address, 'router')
24-
@back_end = @zmq.socket(@worker_address, 'dealer')
25-
@proxy = Thread.new {@zmq.start_proxy(@front_end, @back_end)}
26-
@results = ''
27-
end
28-
29-
#This brings up the broker so that it can receive test run requests.
30-
def start_broker
31-
puts "Broker started..."
32-
@test_socket.receive do |tests|
33-
puts "Received test run request.."
34-
start_test_run(tests)
35-
end
36-
end
37-
38-
private
39-
40-
#This is the start of the test run and is called when the broker receives a test run request
41-
def start_test_run(test_run)
42-
puts 'Starting test run...'
43-
test_run = JSON.parse(test_run)
44-
puts 'Copying over tests..'
45-
tests = test_run["tests"].copy
46-
threads = []
47-
puts "Checking for group configuration...#{test_run['grouped']}"
48-
# puts "This is the test run message:\n"
49-
# puts "#{test_run}"
50-
if test_run["grouped"] || test_run["grouped"].to_s.downcase == 'true'
51-
puts 'Grouping tests....'
52-
tests = group_tests(tests)
53-
end unless test_run["grouped"].nil?
54-
55-
@test_count = test_run["tests"].length
56-
report_test_config(test_run)
57-
58-
if tests.length > @num_of_connections.to_i
59-
puts "Test count was higher than connections. Starting tests and initiating run queue"
60-
1.upto @num_of_connections.to_i do
61-
test = tests.pop
62-
break if test.nil?
63-
threads << run_test(test, test_run)
64-
end
65-
work_queue(threads, tests, test_run)
66-
67-
else
68-
puts "Test count was not higher than the connections. Starting all tests."
69-
1.upto tests.length do
70-
test = tests.pop
71-
break if test.nil?
72-
threads << run_test(test, test_run)
73-
end
74-
75-
end
76-
77-
until threads_dead?(threads) do
78-
# text = "Thread count: #{threads.count} | number of alive: #{num_of_alive(threads)} | number of dead: #{num_of_dead(threads)}"
79-
# $stdout << "\r" + (" " * text.length)
80-
# $stdout << "\r#{text}"
81-
# puts "Threads still working...#{num_of_alive(threads)}"
82-
end
83-
84-
puts "Test run completed....\nGathering results from threads..."
85-
results = get_results(threads)
86-
Kymera::TestResultsCollector.new.finalize_results(test_run["test_count"], test_run["run_id"], results, test_run["runner"], test_run["start_time"])
87-
88-
end
89-
90-
def get_results(threads)
91-
results = ''
92-
threads.each do |t|
93-
results << t.value
94-
end
95-
results
96-
end
97-
98-
def group_tests(tests)
99-
#This creates a group for each of the available connection as specified by the @num_of_connections variable
100-
#It will then iterate over all of the tests and add a test to each of the groups
101-
#at the end of the iteration, if any groups are left empty, they are deleted and the trimmed array is returned
102-
groups = []
103-
104-
puts "Setting group containers..."
105-
1.upto @num_of_connections.to_i do
106-
groups << []
107-
end
108-
109-
puts "Distributing tests..."
110-
while tests.length > 0
111-
groups.each do |group|
112-
test = tests.pop
113-
group << test unless test.nil?
114-
break if test.nil?
115-
end
116-
end
117-
groups.delete_if {|group| group.empty?}
118-
end
119-
120-
121-
#If there are tests left over after the initial test start up, they are placed into a queue. The queue is then worked until all tests in the queue have been executed
122-
def work_queue(threads, tests, options)
123-
until tests.empty?
124-
threads.delete_if {|t| !t.alive?}
125-
if threads.length < @num_of_connections
126-
test = tests.pop
127-
break if test.nil?
128-
threads << run_test(test, options)
129-
end
130-
end
131-
end
132-
133-
def threads_dead?(threads)
134-
result = true
135-
threads.each do |t|
136-
if t.alive?
137-
result = false
138-
break
139-
end
140-
end
141-
result
142-
end
143-
144-
def num_of_dead(threads)
145-
count = 0
146-
threads.each do |t|
147-
count +=1 if !t.alive?
148-
end
149-
count
150-
end
151-
152-
def num_of_alive(threads)
153-
count = 0
154-
threads.each do |t|
155-
count +=1 if t.alive?
156-
end
157-
count
158-
end
159-
160-
#This runs each test individually
161-
def run_test(test, options)
162-
port = @internal_address.split(':')[2]
163-
Thread.new {
164-
zmq = Kymera::SZMQ.new
165-
message = JSON.generate({:test => test, :runner => options["runner"], :options => options["options"], :run_id => options["run_id"],
166-
:test_count => @test_count, :branch => options["branch"], :start_time => options["start_time"]})
167-
socket = zmq.socket("tcp://127.0.0.1:#{port}", 'request')
168-
socket.connect
169-
puts "Sending: #{message}"
170-
results = socket.send_message(message)
171-
socket.close
172-
results
173-
}
174-
end
175-
176-
#This gives a print out of the test run that was received
177-
def report_test_config(test_run)
178-
puts "Running test with the following configuration:"
179-
puts "Branch: #{test_run["branch"]}"
180-
puts "Runner: #{test_run["runner"]}"
181-
puts "Run ID: #{test_run["run_id"]}"
182-
puts "Runner Options: #{test_run["options"]}"
183-
puts "Total number tests: #{test_run["tests"].length}"
184-
end
185-
186-
187-
end
188-
189-
end
1+
# require_relative 'szmq/szmq'
2+
# require 'json'
3+
#
4+
# module Kymera
5+
#
6+
# class Broker
7+
#
8+
# def initialize
9+
# config = Kymera::Config.new
10+
# @zmq = Kymera::SZMQ.new
11+
# Thread.abort_on_exception = true
12+
# #This will change once I get the worker registry up and running. When that is up, there will be one connection spawned for
13+
# #each of the workers that are connected. Edit - Thats actually not sustainable. The number of workers should be able to change
14+
# #dynamically without impact to the broker. An inventory check should be made at the start of every new test run. There should also
15+
# # be some logic to handle the disconnection of workers in the middle of a test run.
16+
# @num_of_connections = config.broker["number_of_connections"]
17+
# #This socket is for getting tests from the client
18+
# @client_address = "tcp://*:#{config.broker["client_listening_port"]}"
19+
# @internal_address = "tcp://*:#{config.broker["internal_worker_port"]}"
20+
# @worker_address = "tcp://*:#{config.broker["worker_listening_port"]}"
21+
# @test_socket = @zmq.socket(@client_address, 'pull')
22+
# @test_socket.bind
23+
# @front_end = @zmq.socket(@internal_address, 'router')
24+
# @back_end = @zmq.socket(@worker_address, 'dealer')
25+
# @proxy = Thread.new {@zmq.start_proxy(@front_end, @back_end)}
26+
# @results = ''
27+
# end
28+
#
29+
# #This brings up the broker so that it can receive test run requests.
30+
# def start_broker
31+
# puts "Broker started..."
32+
# @test_socket.receive do |tests|
33+
# puts "Received test run request.."
34+
# start_test_run(tests)
35+
# end
36+
# end
37+
#
38+
# private
39+
#
40+
# #This is the start of the test run and is called when the broker receives a test run request
41+
# def start_test_run(test_run)
42+
# puts 'Starting test run...'
43+
# test_run = JSON.parse(test_run)
44+
# puts 'Copying over tests..'
45+
# tests = test_run["tests"].copy
46+
# threads = []
47+
# puts "Checking for group configuration...#{test_run['grouped']}"
48+
# # puts "This is the test run message:\n"
49+
# # puts "#{test_run}"
50+
# if test_run["grouped"] || test_run["grouped"].to_s.downcase == 'true'
51+
# puts 'Grouping tests....'
52+
# tests = group_tests(tests)
53+
# end unless test_run["grouped"].nil?
54+
#
55+
# @test_count = test_run["tests"].length
56+
# report_test_config(test_run)
57+
#
58+
# if tests.length > @num_of_connections.to_i
59+
# puts "Test count was higher than connections. Starting tests and initiating run queue"
60+
# 1.upto @num_of_connections.to_i do
61+
# test = tests.pop
62+
# break if test.nil?
63+
# threads << run_test(test, test_run)
64+
# end
65+
# work_queue(threads, tests, test_run)
66+
#
67+
# else
68+
# puts "Test count was not higher than the connections. Starting all tests."
69+
# 1.upto tests.length do
70+
# test = tests.pop
71+
# break if test.nil?
72+
# threads << run_test(test, test_run)
73+
# end
74+
#
75+
# end
76+
#
77+
# until threads_dead?(threads) do
78+
# # text = "Thread count: #{threads.count} | number of alive: #{num_of_alive(threads)} | number of dead: #{num_of_dead(threads)}"
79+
# # $stdout << "\r" + (" " * text.length)
80+
# # $stdout << "\r#{text}"
81+
# # puts "Threads still working...#{num_of_alive(threads)}"
82+
# end
83+
#
84+
# puts "Test run completed....\nGathering results from threads..."
85+
# results = get_results(threads)
86+
# Kymera::TestResultsCollector.new.finalize_results(test_run["test_count"], test_run["run_id"], results, test_run["runner"], test_run["start_time"])
87+
#
88+
# end
89+
#
90+
# def get_results(threads)
91+
# results = ''
92+
# threads.each do |t|
93+
# results << t.value
94+
# end
95+
# results
96+
# end
97+
#
98+
# def group_tests(tests)
99+
# #This creates a group for each of the available connection as specified by the @num_of_connections variable
100+
# #It will then iterate over all of the tests and add a test to each of the groups
101+
# #at the end of the iteration, if any groups are left empty, they are deleted and the trimmed array is returned
102+
# groups = []
103+
#
104+
# puts "Setting group containers..."
105+
# 1.upto @num_of_connections.to_i do
106+
# groups << []
107+
# end
108+
#
109+
# puts "Distributing tests..."
110+
# while tests.length > 0
111+
# groups.each do |group|
112+
# test = tests.pop
113+
# group << test unless test.nil?
114+
# break if test.nil?
115+
# end
116+
# end
117+
# groups.delete_if {|group| group.empty?}
118+
# end
119+
#
120+
#
121+
# #If there are tests left over after the initial test start up, they are placed into a queue. The queue is then worked until all tests in the queue have been executed
122+
# def work_queue(threads, tests, options)
123+
# until tests.empty?
124+
# threads.delete_if {|t| !t.alive?}
125+
# if threads.length < @num_of_connections
126+
# test = tests.pop
127+
# break if test.nil?
128+
# threads << run_test(test, options)
129+
# end
130+
# end
131+
# end
132+
#
133+
# def threads_dead?(threads)
134+
# result = true
135+
# threads.each do |t|
136+
# if t.alive?
137+
# result = false
138+
# break
139+
# end
140+
# end
141+
# result
142+
# end
143+
#
144+
# def num_of_dead(threads)
145+
# count = 0
146+
# threads.each do |t|
147+
# count +=1 if !t.alive?
148+
# end
149+
# count
150+
# end
151+
#
152+
# def num_of_alive(threads)
153+
# count = 0
154+
# threads.each do |t|
155+
# count +=1 if t.alive?
156+
# end
157+
# count
158+
# end
159+
#
160+
# #This runs each test individually
161+
# def run_test(test, options)
162+
# port = @internal_address.split(':')[2]
163+
# Thread.new {
164+
# zmq = Kymera::SZMQ.new
165+
# message = JSON.generate({:test => test, :runner => options["runner"], :options => options["options"], :run_id => options["run_id"],
166+
# :test_count => @test_count, :branch => options["branch"], :start_time => options["start_time"]})
167+
# socket = zmq.socket("tcp://127.0.0.1:#{port}", 'request')
168+
# socket.connect
169+
# puts "Sending: #{message}"
170+
# results = socket.send_message(message)
171+
# socket.close
172+
# results
173+
# }
174+
# end
175+
#
176+
# #This gives a print out of the test run that was received
177+
# def report_test_config(test_run)
178+
# puts "Running test with the following configuration:"
179+
# puts "Branch: #{test_run["branch"]}"
180+
# puts "Runner: #{test_run["runner"]}"
181+
# puts "Run ID: #{test_run["run_id"]}"
182+
# puts "Runner Options: #{test_run["options"]}"
183+
# puts "Total number tests: #{test_run["tests"].length}"
184+
# end
185+
#
186+
#
187+
# end
188+
#
189+
# end

‎lib/kymera/mongo_driver.rb

+6
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ def get_collection(collection_name = @collection)
8787
docs
8888
end
8989

90+
def update(id, attribute_hash)
91+
coll = @db_client["#{@collection}"]
92+
# binding.pry
93+
coll.update({"#{@collection.chop}_id".to_sym => id}, {"$set" => attribute_hash})
94+
end
95+
9096

9197
end
9298

‎lib/kymera/results_bus.rb

+23-7
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,34 @@ module Kymera
66
class ResultsBus
77

88
def initialize
9-
config = Kymera::Config.new
9+
@config = Kymera::Config.new
1010
@zmq = Kymera::SZMQ.new
1111
# @incoming_socket = @zmq.socket("tcp://*:7000", 'xsub')
12-
@incoming_socket = @zmq.socket("tcp://*:#{config.result_bus["pub_port"]}", 'xsub')
13-
@outgoing_socket = @zmq.socket("tcp://*:#{config.result_bus["sub_port"]}", 'xpub')
12+
@incoming_socket = @zmq.socket("tcp://*:#{@config.result_bus["pub_port"]}", 'xsub')
13+
@outgoing_socket = @zmq.socket("tcp://*:#{@config.result_bus["sub_port"]}", 'xpub')
1414
# @outgoing_socket = @zmq.socket("tcp://*:7001", 'xpub')
1515
end
1616

17-
def start_bus
18-
puts "Results bus started..."
19-
puts "Listening for stuff"
20-
@zmq.start_pub_sub_proxy(@incoming_socket, @outgoing_socket)
17+
def start_bus(logging = false)
18+
puts @config.result_bus["pub_port"]
19+
puts @config.result_bus["sub_port"]
20+
if logging
21+
puts "Results bus started..."
22+
puts "Listening for stuff"
23+
puts "Logging is turned on"
24+
logging_socket = @zmq.socket("tcp://*:7010", 'push')
25+
t = Thread.new {@zmq.start_pub_sub_proxy(@incoming_socket, @outgoing_socket, logging_socket)}
26+
output_socket = @zmq.socket("tcp://127.0.0.1:7010", 'pull')
27+
output_socket.connect
28+
output_socket.receive {|message|
29+
puts message
30+
}
31+
t.join
32+
else
33+
puts "Results bus started..."
34+
puts "Listening for stuff"
35+
@zmq.start_pub_sub_proxy(@incoming_socket, @outgoing_socket)
36+
end
2137
end
2238

2339

‎lib/kymera/szmq/szmq.rb

+9-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def start_proxy(frontend_socket, backend_socket)
4747
#end
4848
end
4949

50-
def start_pub_sub_proxy(front_end, back_end)
50+
def start_pub_sub_proxy(front_end, back_end, capture = nil)
5151
trap ("INT") do
5252
puts "\nStopping proxy..."
5353
front_end.close
@@ -58,7 +58,14 @@ def start_pub_sub_proxy(front_end, back_end)
5858
front_end.bind
5959
back_end.bind
6060

61-
ZMQ::Device.new(front_end.send(:get_socket), back_end.send(:get_socket))
61+
if capture.nil?
62+
p "No capture"
63+
ZMQ::Device.new(front_end.send(:get_socket), back_end.send(:get_socket))
64+
else
65+
p "Capture"
66+
capture.bind
67+
ZMQ::Device.new(front_end.send(:get_socket), back_end.send(:get_socket), capture.send(:get_socket))
68+
end
6269

6370

6471
end

‎lib/kymera/test_results_collector.rb

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ def listen
3737
def finalize_results(test_count, run_id, results, runner, start_time)
3838
if runner.downcase == 'cucumber'
3939

40+
puts test_count
41+
puts run_id
42+
puts results
43+
puts runner
44+
puts start_time
45+
4046
begin
4147
puts "Summarizing results.."
4248
r_results = Kymera::Cucumber::ResultsParser.summarize_results(results)
@@ -57,6 +63,7 @@ def finalize_results(test_count, run_id, results, runner, start_time)
5763
rescue => e
5864
puts "There was an error in the logging process:"
5965
puts e
66+
puts e.backtrace
6067
ensure
6168
run_id = "end_#{run_id}"
6269
puts "Sending results to client...#{run_id}"

‎lib/kymera/v2/broker.rb

+294
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
# require_relative 'szmq/szmq'
2+
require 'json'
3+
4+
module Kymera
5+
6+
class Broker
7+
8+
attr_accessor :config, :context, :registry, :results, :log, :listening_thread, :host_name, :channel, :status, :bundle_id, :run_ids
9+
def initialize(config, context, registry)
10+
@config = config
11+
@context = context
12+
@registry = registry
13+
@results = ''
14+
@log = {}
15+
@listening_thread = nil
16+
@monitor_thread = nil
17+
@host_name = Kymera.host_name
18+
@channel = config.broker["channel"]
19+
@status = 'stopped'
20+
@bundle_id = 0
21+
@run_ids = []
22+
@host_name = Kymera.host_name
23+
end
24+
25+
26+
def listen
27+
# connect to bus and listen for requests
28+
# put this in a thread so it is non blocking
29+
monitor_test_runs
30+
if @listening_thread != nil && @listening_thread.status != 'dead'
31+
puts "The broker is already listening"
32+
return
33+
end
34+
# p 10
35+
@listening_thread = Thread.new(@context) { |szmq_context|
36+
# p 11
37+
@sub_socket = szmq_context.socket("tcp://127.0.0.1:7001", "sub")
38+
# p 12
39+
@pub_socket = szmq_context.socket("tcp://127.0.0.1:7000", "pub")
40+
# p 13
41+
@pub_socket.connect
42+
sleep 1
43+
# p 14
44+
@log = {}
45+
puts "Waiting for messages on #{@channel} channel..."
46+
# p 15
47+
@sub_socket.subscribe(@channel) { |channel, message|
48+
begin
49+
# p 16
50+
message = JSON.parse message
51+
# p "Got a message: #{message}"
52+
# p "These are the keys########################################"
53+
# p message.keys
54+
# p 17
55+
if message.has_key?("test")
56+
# puts "got message"
57+
# puts message["test"]["run_id"]
58+
@pub_socket.publish_message message["test"]["sender_id"], 'test received'
59+
# p 18
60+
elsif message.has_key?("test_run")
61+
# p 19
62+
run_id = message["test_run"]["sender_id"] + Time.now.to_i.to_s
63+
@log[run_id] = {}
64+
@log[run_id]["results"] = ''
65+
@log[run_id]["test_count"] = message["test_run"]["test"].length
66+
@log[run_id]["runner"] = message["test_run"]["runner"]
67+
@log[run_id]["start_time"] = message["test_run"]["start_time"]
68+
@run_ids << run_id
69+
# test_count = message["test_run"]["test"].length
70+
# check to see if this broker is currently in the middle of a test run (@status == 'busy')
71+
# if it is busy, send a message back to the user telling them there is currently a test run in progress
72+
# possibly add functionality for queuing test runs later
73+
# need to add a check to see if there are any nodes actually registered in the system. If there are not, I need to inform the client
74+
if @status == 'busy'
75+
# p 20
76+
@pub_socket.publish_message(message["test_run"]["sender_id"], JSON.generate(:error => "There is currently a test run in progress"))
77+
else
78+
# p 21
79+
@status = "busy"
80+
bundle_and_run_tests(message, run_id, @pub_socket)
81+
# p 22
82+
while !message["test_run"]["test"].empty? do
83+
# p 23
84+
# binding.pry
85+
bundle_and_run_tests(message, run_id, @pub_socket)
86+
sleep 5
87+
end
88+
# sleep 20
89+
end
90+
# if there are no runs currently in progress get a list of registered nodes
91+
# check to see if the registered nodes are available (send a message to them asking for their info (name, number of workers, and so on))
92+
# if they are none available, report back that no nodes are currently available for test run (this may not be necessary but we'll see)
93+
# wait for a predetermined amount of time for responses from the nodes and collect the responses
94+
# for the nodes that replied, bundle a group of tests based on the maximum number of workers for that node
95+
# record those tests in a log and which node they are going to and send them (might useful to include a batch number)
96+
# after getting a reply with the results for a batch, record the results of that batch and repeat the above step until the test array is empty
97+
# after all tests are accounted for, send the collected results to the results parser for parsing
98+
# send the parsed results back to the client
99+
# send the original results to be parsed by the html parser
100+
# send those html parsed results to the database for use by the Leo web application
101+
102+
#this is going to fail results and batches are at the same level. need to add a check for this
103+
104+
elsif message.has_key?("stop")
105+
# p 28
106+
@status = 'stopped'
107+
puts "Got the shutdown signal"
108+
Thread.kill(Thread.current)
109+
110+
elsif message.has_key?("results")
111+
# p 29
112+
# p "############Got results, Processing them part 1#################"
113+
# p "These are the log keys"
114+
# p @log.keys
115+
# p "These are the message keys"
116+
# p @log[message["results"]["run_id"]][message["results"]["bundle_id"].to_i]
117+
# p message
118+
if @log.has_key?(message["results"]["run_id"])
119+
# p "############Got results, Processing them part 2#################"
120+
@log[message["results"]["run_id"]][message["results"]["bundle_id"].to_i][:results] = message["results"]["text"]
121+
@log[message["results"]["run_id"]][message["results"]["bundle_id"].to_i][:end_time] = Time.now
122+
end
123+
# p 30
124+
else
125+
# p 31
126+
# p "These are the keys########################################"
127+
# p message.keys
128+
# puts "Got the following message:"
129+
# puts "message: #{message}"
130+
@pub_socket.publish_message(message[message.keys[0]]["run_id"], "test received")
131+
end
132+
133+
# p 32
134+
rescue => e
135+
# p 33
136+
puts "There was an error parsing the message on the broker: #{e}"
137+
puts e.backtrace
138+
end
139+
}
140+
# @socket.close
141+
}
142+
# this will wait for the thread to go to sleep before returning. This is to solve a racing condition where the status
143+
# was not set correctly
144+
# p 34
145+
while @listening_thread.status != 'sleep' || @listening_thread.status.nil?
146+
# p @listening_thread.status
147+
# p @listening_thread.value
148+
sleep 0.1
149+
end
150+
@status = "ready"
151+
end
152+
153+
def shutdown
154+
p "sending shutdown signal"
155+
@status = "stopped"
156+
Thread.kill(@listening_thread)
157+
end
158+
159+
160+
161+
private
162+
163+
def monitor_test_runs
164+
# p "#######################Starting the monitoring of test runs##########################"
165+
@monitor_thread = Thread.new {
166+
# p "Starting the loop"
167+
loop do
168+
# p "Inside the loop"
169+
# begin
170+
if @log.empty?
171+
# p "Log is empty"
172+
173+
else
174+
# p "This is the log####################"
175+
# p @log
176+
# p "###################################"
177+
begin
178+
@log.each do |run_id, bundle|
179+
#if run_id[:status] != "done"
180+
if @log[run_id][:status] != "complete"
181+
182+
done = true
183+
bundle.each do |k,v|
184+
# puts "####################Bundles####################"
185+
# puts k
186+
# puts v
187+
# puts k.class
188+
# puts "################################################"
189+
if k.class == Fixnum
190+
if v[:end_time].nil?
191+
done = false
192+
end
193+
end
194+
end
195+
196+
if done
197+
198+
# puts "the log ########################"
199+
# puts @log
200+
# puts @log.keys
201+
# p 24
202+
@log[run_id].each do |key, b|
203+
# p 25
204+
# p "#########The Run was done processing data ##############"
205+
# p key
206+
# p b
207+
# p "#######################"
208+
if key.class == Fixnum
209+
@log[run_id]["results"] << b[:results]
210+
end
211+
end unless @log[run_id].nil?
212+
# p "#################################These are the results ##################################"
213+
# p @log[run_id]["results"]
214+
Kymera::TestResultsCollector.new.finalize_results(@log[run_id]["test_count"],
215+
run_id,
216+
@log[run_id]["results"],
217+
@log[run_id]["runner"],
218+
@log[run_id]["start_time"])
219+
@log[run_id][:status] = "complete"
220+
@status = "ready"
221+
end
222+
223+
end
224+
end
225+
rescue => e
226+
puts e
227+
puts e.backtrace
228+
end
229+
end
230+
sleep 2
231+
end
232+
}
233+
234+
end
235+
236+
def bundle_and_run_tests(test_run, run_id, socket, nodes=nil)
237+
# p 103
238+
# p test_run
239+
# p run_id
240+
# p socket
241+
# p nodes
242+
nodes ||= @registry.get_registered_nodes
243+
# p nodes
244+
nodes.each do |node|
245+
# p 104
246+
unless node["status"] == "busy"
247+
bundle = []
248+
# p '################################'
249+
# p "These are the number of workers"
250+
# p node
251+
# p node["num_of_workers"]
252+
# p '################################'
253+
0.upto node["num_of_workers"].to_i do
254+
test = test_run["test_run"]["test"].pop
255+
break if test.nil?
256+
bundle << test
257+
end
258+
# p 105
259+
start_time = Time.now
260+
socket.publish_message(node["node_id"], JSON.generate(:test_run => {:test => bundle, :run_id => run_id, :bundle_id => @bundle_id, :sender_id => @channel,
261+
:options => test_run["test_run"]["options"], :runner => test_run["test_run"]["runner"]}))
262+
# p 106
263+
@log[run_id][@bundle_id] = {:node_id => node["node_id"], :start_time => start_time, :end_time => nil, :test_count => bundle.length, :test_bundle => bundle, :results => '', :status => "in progress"}
264+
@bundle_id +=1
265+
# p 107
266+
end
267+
268+
end
269+
end
270+
271+
# #This is the start of the test run and is called when the broker receives a test run request
272+
# def start_test_run(test_run)
273+
# puts 'Starting test run...'
274+
# test_run = JSON.parse(test_run)
275+
#
276+
# Kymera::TestResultsCollector.new.finalize_results(test_run["test_count"], test_run["run_id"], results, test_run["runner"], test_run["start_time"])
277+
#
278+
# end
279+
#
280+
#
281+
# #This gives a print out of the test run that was received
282+
# def report_test_config(test_run)
283+
# puts "Running test with the following configuration:"
284+
# puts "Branch: #{test_run["branch"]}"
285+
# puts "Runner: #{test_run["runner"]}"
286+
# puts "Run ID: #{test_run["run_id"]}"
287+
# puts "Runner Options: #{test_run["options"]}"
288+
# puts "Total number tests: #{test_run["tests"].length}"
289+
# end
290+
291+
292+
end
293+
294+
end

‎lib/kymera/v2/node.rb

+17-34
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ def start_test_run(test_run)
6565
test = tests.pop
6666
break if test.nil?
6767
log_id = Time.now.to_s
68+
log_id << rand(999).to_s
6869
log[log_id] = {:status => "in progress", :worker => worker[:worker].id, :results =>''}
6970
tasks << Thread.new(worker, log, test, log_id, test_run) { |wkr, _log, _test, _log_id, _test_run|
7071
puts "sending test to the worker"
@@ -127,7 +128,7 @@ def system
127128
# worker_id/hostname
128129
# RAM?
129130
# Ruby version?
130-
{:node_id => @host_name, :ip_address => Kymera.ip_address, :port => @port, :processor_count => @num_of_workers, :node_os => Kymera.os, :ruby_version => Kymera.ruby_version}
131+
{:node_id => @host_name, :ip_address => Kymera.ip_address, :port => @port, :processor_count => @num_of_workers, :node_os => Kymera.os, :ruby_version => Kymera.ruby_version, :status => @status}
131132
end
132133

133134
def listen
@@ -138,6 +139,8 @@ def listen
138139
return
139140
end
140141

142+
puts "Warning: This node does not appear to be registered. Please make sure to register the node in order to properly receive messages from the test syste" unless @registered
143+
141144
@listening_thread = Thread.new(@context) { |szmq_context|
142145
@sub_socket = szmq_context.socket("tcp://127.0.0.1:7001", "sub")
143146
# sleep 3
@@ -149,11 +152,11 @@ def listen
149152
@sub_socket.subscribe(@host_name) { |channel, message|
150153
# puts "got message.....parsing it..."
151154
begin
152-
puts "This is the message as it came in:"
153-
puts message
155+
# puts "This is the message as it came in:"
156+
# puts message
154157
message = JSON.parse message
155-
puts "This is the message after it was parsed: "
156-
puts message
158+
# puts "This is the message after it was parsed: "
159+
# puts message
157160
if message.has_key?("test")
158161
puts "got message"
159162
puts message["test"]["run_id"]
@@ -163,16 +166,20 @@ def listen
163166
@pub_socket.publish_message(message["config"]["run_id"], JSON.generate({:config=>{:message=>"ready"}}))
164167
elsif message.has_key?("test_run")
165168
@status = "busy"
166-
puts "This is the message when it gets inside the test_run condition:"
167-
puts message
168-
results = JSON.generate({:results => {:run_id => message["test_run"]["run_id"], :text => start_test_run(message)}})
169-
@pub_socket.publish_message(message["test_run"]["run_id"], results)
169+
@registry.update_node_value(@host_name, {:status => "busy"})
170+
# puts "This is the message when it gets inside the test_run condition:"
171+
# puts message
172+
results = JSON.generate({:results => {:run_id => message["test_run"]["run_id"], :bundle_id => message["test_run"]["bundle_id"], :text => start_test_run(message)}})
170173
@status = "ready"
174+
@registry.update_node_value(@host_name, {:status => "ready"})
175+
@pub_socket.publish_message(message["test_run"]["sender_id"], results)
171176
elsif message.has_key?("stop")
177+
p "Received shutdown signal. Shutting down."
172178
@status = 'stopped'
179+
@registry.update_node_value(@host_name, {:status => "stopped"})
173180
Thread.kill(Thread.current)
174181
else
175-
puts "Got the following message:"
182+
puts "Got the following message on the node:"
176183
puts "message: #{message}"
177184
@pub_socket.publish_message(message[message.keys[0]]["run_id"], "test received")
178185
end
@@ -267,30 +274,6 @@ def update_current_branch
267274
end
268275
end
269276

270-
# class Test
271-
#
272-
# attr_accessor :test, :worker, :status
273-
#
274-
# def initialize(test, worker)
275-
# @test = test
276-
# @worker = worker
277-
# end
278-
#
279-
#
280-
# def self.assign_test(test, worker)
281-
# Test.new(test, worker)
282-
# end
283-
#
284-
# def to_hash
285-
# {:test => @test, :worker => @worker, :status => status}
286-
# end
287-
#
288-
# def to_json
289-
# JSON.generate(self.to_hash)
290-
# end
291-
#
292-
#
293-
# end
294277

295278

296279

‎lib/kymera/v2/registry.rb

+10
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ def initialize(address = 'localhost', port = 27017, database = 'default_db', col
2121
# @todo need to add a check for the nodes existance before trying to register. We dont want multiple nodes of the same name in the database
2222
def register_node(node)
2323
mongo_driver = Kymera::MongoDriver.new(address, port, database, 'nodes')
24+
if mongo_driver.exists?("node_id" => node[:node_id])
25+
puts "#{node[:node_id]} is already registered"
26+
return true
27+
end
2428
register = {}
2529
register[:node_id] = node[:node_id]
30+
register[:status] = node[:status]
2631
register[:ip_address] = node[:ip_address]
2732
register[:port] = node[:port]
2833
register[:register_date] = Time.now.to_datetime
@@ -103,6 +108,11 @@ def get_registered_nodes
103108
@registered_nodes
104109
end
105110

111+
def update_node_value(node_id, attribute_hash)
112+
mongo_driver = Kymera::MongoDriver.new(address, port, database, 'nodes')
113+
mongo_driver.update(node_id, attribute_hash)
114+
end
115+
106116

107117
private
108118

‎lib/kymera/v2/worker_v2.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def listen
5555
@socket.close
5656
# need to kill thread?
5757
@status = "stopped"
58-
return
58+
break
5959
elsif message.has_key? "test_run"
6060
@status = "running test"
6161
@socket.send_message run_test(message)

‎lib/spec/send_bus_data.rb

+8-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@
44
#
55
pub_socket = zmq.socket('tcp://127.0.0.1:7000', 'pub')
66
pub_socket.connect
7-
pub_socket.publish_message('results', 'these are results')
7+
sleep 2
8+
pub_socket.publish_message('broker', JSON.generate({:test_run => {:test => ['~/apollo/source/integration_tests/features/login_and_session/login.feature:9',
9+
'~/apollo/source/integration_tests/features/login_and_session/login.feature:13',
10+
'~/apollo/source/integration_tests/features/login_and_session/login.feature:17'],
11+
:runner => "cucumber",
12+
:options => ['-p default'],
13+
:sender_id => "test",
14+
:start_time => Time.now.to_s}}))
815

916
#context = ZMQ::Context.new
1017
#socket = context.socket(ZMQ::PUB)

‎lib/spec/v2/broker_spec.rb

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
require_relative '../../../lib/kymera'
2+
require 'pry'
3+
4+
describe Kymera::Broker do
5+
6+
it 'should start with default values' do
7+
context = Kymera::SZMQ.new
8+
registry = Kymera::Registry.new('localhost', 27017, 'kymera', 'nodes')
9+
config = Kymera::Config.new
10+
node = Kymera::Node.new(config)
11+
p 1
12+
node.listen
13+
p 2
14+
broker = Kymera::Broker.new(config, context, registry)
15+
p 3
16+
broker.listen
17+
p 4
18+
sleep 1
19+
node.shutdown_node
20+
p 5
21+
broker.shutdown
22+
p 6
23+
sleep 10
24+
raise "It no work"
25+
end
26+
27+
it "should accept and complete a test run" do
28+
context = Kymera::SZMQ.new
29+
registry = Kymera::Registry.new('localhost', 27017, 'kymera', 'nodes')
30+
config = Kymera::Config.new
31+
node = Kymera::Node.new(config)
32+
node.register_node
33+
node.listen
34+
35+
broker = Kymera::Broker.new(config, context, registry)
36+
broker.listen
37+
sleep 3
38+
test = JSON.generate({:test_run => {:test => ['~/apollo/source/integration_tests/features/login_and_session/login.feature:9', '~/apollo/source/integration_tests/features/login_and_session/login.feature:13', '~/apollo/source/integration_tests/features/login_and_session/login.feature:17'], :runner => "cucumber", :options => ['-p default'], :sender_id => "test"}})
39+
40+
socket = context.socket("tcp://127.0.0.1:7000", "pub")
41+
# socket.connect
42+
p 101
43+
sub_socket = context.socket("tcp://127.0.0.1:7001", "sub")
44+
p 102
45+
46+
t = Thread.new(sub_socket) {|ssocket|
47+
48+
ssocket.subscribe('test') {|channel, message|
49+
message = JSON.parse(message)
50+
puts message
51+
52+
}
53+
}
54+
sleep 5
55+
socket.publish_message('broker', test)
56+
57+
t.join(60)
58+
59+
end
60+
61+
62+
end

‎lib/spec/v2/kymera_config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ broker:
77
worker_listening_port: '5552'
88
internal_worker_port: '5551'
99
number_of_connections: 1
10+
channel: 'broker'
1011
worker:
1112
broker_address: tcp://127.0.0.1:5552
1213
result_bus_address: tcp://127.0.0.1:7000

‎lib/spec/v2/node_spec.rb

+45-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
# sleep 4
114114
socket.publish_message(Kymera.host_name, JSON.generate({:test => {:run_id=>"test"}}))
115115
t.join(10)
116+
Thread.kill(t)
116117
expect(result).to eq true
117118
node.shutdown_node
118119
end
@@ -149,12 +150,55 @@
149150

150151
}
151152
sleep 1
152-
message = JSON.generate ({:config => {:branch => "default", :run_id => 'test'}})
153+
message = JSON.generate ({:config => {:branch => "develop", :run_id => 'test'}})
153154
puts "sending config..."
154155
socket.publish_message(Kymera.host_name, message)
155156
puts "Sending test run..."
156157
t.join(90)
157158
socket.close
159+
node.shutdown_node
160+
expect(@results).to eq true
161+
end
162+
163+
it 'should accept a multiple tests in a run' do
164+
context = Kymera::SZMQ.new
165+
node = Kymera::Node.new(Kymera::Config.new)
166+
node.register_node
167+
node.listen
168+
socket = context.socket("tcp://127.0.0.1:7000", "pub")
169+
socket.connect
170+
@results = false
171+
t = Thread.new(socket) { |p_socket|
172+
ctx = Kymera::SZMQ.new
173+
sub_socket = ctx.socket("tcp://127.0.0.1:7001", "sub")
174+
# puts "waiting for reply"
175+
sub_socket.subscribe("test") {|channel, message|
176+
# puts "got reply"
177+
# puts channel
178+
puts message
179+
message = JSON.parse message
180+
if message.keys.include? "config"
181+
puts "Sending test..."
182+
test = JSON.generate({:test_run => {:test => ['~/apollo/source/integration_tests/features/login_and_session/login.feature:9', '~/apollo/source/integration_tests/features/login_and_session/login.feature:13', '~/apollo/source/integration_tests/features/login_and_session/login.feature:17'], :runner => "cucumber", :options => ['-p default'], :sender_id => "test"}})
183+
puts test
184+
p_socket.publish_message(Kymera.host_name, test)
185+
elsif message.keys.include? "results"
186+
puts message["results"]["text"]
187+
@results = true
188+
sub_socket.close
189+
Thread.kill(Thread.current)
190+
end
191+
}
192+
193+
}
194+
sleep 1
195+
message = JSON.generate ({:config => {:branch => "develop", :run_id => 'test'}})
196+
puts "sending config..."
197+
socket.publish_message(Kymera.host_name, message)
198+
puts "Sending test run..."
199+
t.join(180)
200+
socket.close
201+
node.shutdown_node
158202
expect(@results).to eq true
159203
end
160204

0 commit comments

Comments
 (0)
Please sign in to comment.