gorgon 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +3 -3
- data/bin/gorgon +8 -0
- data/lib/gorgon/colors.rb +4 -0
- data/lib/gorgon/job_definition.rb +1 -1
- data/lib/gorgon/listener.rb +23 -4
- data/lib/gorgon/originator.rb +14 -1
- data/lib/gorgon/originator_protocol.rb +11 -4
- data/lib/gorgon/ping_service.rb +66 -0
- data/lib/gorgon/progress_bar_view.rb +9 -10
- data/lib/gorgon/source_tree_syncer.rb +18 -1
- data/lib/gorgon/version.rb +1 -1
- data/lib/gorgon/worker.rb +1 -6
- data/lib/gorgon/worker_manager.rb +9 -0
- data/spec/job_definition_spec.rb +1 -2
- data/spec/listener_spec.rb +29 -12
- data/spec/originator_protocol_spec.rb +17 -1
- data/spec/originator_spec.rb +2 -1
- data/spec/ping_service_spec.rb +44 -0
- data/spec/source_tree_syncer_spec.rb +18 -0
- metadata +5 -2
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
gorgon (0.
|
4
|
+
gorgon (0.2.0)
|
5
5
|
amqp (~> 0.9.7)
|
6
6
|
awesome_print
|
7
7
|
bunny (~> 0.8.0)
|
@@ -18,12 +18,12 @@ GEM
|
|
18
18
|
amq-client (0.9.4)
|
19
19
|
amq-protocol (>= 0.9.4)
|
20
20
|
eventmachine
|
21
|
-
amq-protocol (0.9.
|
21
|
+
amq-protocol (0.9.5)
|
22
22
|
amqp (0.9.7)
|
23
23
|
amq-client (~> 0.9.4)
|
24
24
|
amq-protocol (>= 0.9.4)
|
25
25
|
eventmachine
|
26
|
-
awesome_print (1.0
|
26
|
+
awesome_print (1.1.0)
|
27
27
|
bunny (0.8.0)
|
28
28
|
colorize (0.5.8)
|
29
29
|
diff-lcs (1.1.3)
|
data/bin/gorgon
CHANGED
@@ -2,6 +2,7 @@ require "rubygems"
|
|
2
2
|
require 'gorgon/originator'
|
3
3
|
require 'gorgon/listener'
|
4
4
|
require 'gorgon/worker_manager'
|
5
|
+
require 'gorgon/ping_service'
|
5
6
|
require 'gorgon/version'
|
6
7
|
|
7
8
|
WELCOME_MSG = "Welcome to Gorgon #{Gorgon::VERSION}"
|
@@ -26,10 +27,15 @@ def manage_workers
|
|
26
27
|
exit
|
27
28
|
end
|
28
29
|
|
30
|
+
def ping_listeners
|
31
|
+
PingService.new.ping_listeners
|
32
|
+
end
|
33
|
+
|
29
34
|
def usage
|
30
35
|
#print instructions on how to use gorgon
|
31
36
|
puts "\tstart - remotely runs all tests specified in gorgon.json"
|
32
37
|
puts "\tlisten - starts a listener process using the settings in gorgon_listener.json"
|
38
|
+
puts "\tping - pings listeners and shows hosts and gorgon's version they are running"
|
33
39
|
end
|
34
40
|
|
35
41
|
puts WELCOME_MSG
|
@@ -43,6 +49,8 @@ when "listen"
|
|
43
49
|
listen
|
44
50
|
when "manage_workers"
|
45
51
|
manage_workers
|
52
|
+
when "ping"
|
53
|
+
ping_listeners
|
46
54
|
when "help"
|
47
55
|
usage
|
48
56
|
else
|
@@ -19,6 +19,6 @@ class JobDefinition
|
|
19
19
|
|
20
20
|
#This can probably be done with introspection somehow, but this is way easier despite being very verbose
|
21
21
|
def to_hash
|
22
|
-
{:file_queue_name => @file_queue_name, :reply_exchange_name => @reply_exchange_name, :source_tree_path => @source_tree_path, :sync_exclude => @sync_exclude, :callbacks => @callbacks}
|
22
|
+
{:type => "job_definition", :file_queue_name => @file_queue_name, :reply_exchange_name => @reply_exchange_name, :source_tree_path => @source_tree_path, :sync_exclude => @sync_exclude, :callbacks => @callbacks}
|
23
23
|
end
|
24
24
|
end
|
data/lib/gorgon/listener.rb
CHANGED
@@ -21,7 +21,7 @@ class Listener
|
|
21
21
|
@listener_config_filename = Dir.pwd + "/gorgon_listener.json"
|
22
22
|
initialize_logger configuration[:log_file]
|
23
23
|
|
24
|
-
log "Listener #{Gorgon::VERSION}
|
24
|
+
log "Listener #{Gorgon::VERSION} initializing"
|
25
25
|
connect
|
26
26
|
initialize_personal_job_queue
|
27
27
|
end
|
@@ -48,16 +48,26 @@ class Listener
|
|
48
48
|
def poll
|
49
49
|
message = @job_queue.pop
|
50
50
|
return false if message[:payload] == :queue_empty
|
51
|
+
log "Received: #{message[:payload]}"
|
51
52
|
|
52
|
-
|
53
|
+
handle_request message[:payload]
|
53
54
|
|
54
55
|
log "Waiting for more jobs..."
|
55
56
|
return true
|
56
57
|
end
|
57
58
|
|
58
|
-
def
|
59
|
-
log "Job received: #{json_payload}"
|
59
|
+
def handle_request json_payload
|
60
60
|
payload = Yajl::Parser.new(:symbolize_keys => true).parse(json_payload)
|
61
|
+
|
62
|
+
case payload[:type]
|
63
|
+
when "job_definition"
|
64
|
+
run_job(payload)
|
65
|
+
when "ping"
|
66
|
+
respong_to_ping payload[:reply_exchange_name]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def run_job(payload)
|
61
71
|
@job_definition = JobDefinition.new(payload)
|
62
72
|
@reply_exchange = @bunny.exchange(@job_definition.reply_exchange_name)
|
63
73
|
|
@@ -140,6 +150,15 @@ class Listener
|
|
140
150
|
end
|
141
151
|
end
|
142
152
|
|
153
|
+
def respong_to_ping reply_exchange_name
|
154
|
+
reply = {:type => "ping_response", :hostname => Socket.gethostname,
|
155
|
+
:version => Gorgon::VERSION}
|
156
|
+
reply_exchange = @bunny.exchange(reply_exchange_name, :auto_delete => true)
|
157
|
+
|
158
|
+
log "Sending #{reply}"
|
159
|
+
reply_exchange.publish(Yajl::Encoder.encode(reply))
|
160
|
+
end
|
161
|
+
|
143
162
|
def connection_information
|
144
163
|
configuration[:connection]
|
145
164
|
end
|
data/lib/gorgon/originator.rb
CHANGED
@@ -123,11 +123,24 @@ class Originator
|
|
123
123
|
def job_definition
|
124
124
|
job_config = configuration[:job]
|
125
125
|
if !job_config.has_key?(:source_tree_path)
|
126
|
-
job_config[:source_tree_path] = "#{Etc.getlogin}@#{
|
126
|
+
job_config[:source_tree_path] = "#{Etc.getlogin}@#{local_ip_addr}:#{Dir.pwd}"
|
127
127
|
end
|
128
128
|
JobDefinition.new(configuration[:job])
|
129
129
|
end
|
130
130
|
|
131
|
+
private
|
132
|
+
|
133
|
+
def local_ip_addr
|
134
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
135
|
+
|
136
|
+
UDPSocket.open do |s|
|
137
|
+
s.connect '64.59.144.16', 1
|
138
|
+
s.addr.last
|
139
|
+
end
|
140
|
+
ensure
|
141
|
+
Socket.do_not_reverse_lookup = orig
|
142
|
+
end
|
143
|
+
|
131
144
|
def configuration
|
132
145
|
@configuration ||= load_configuration_from_file("gorgon.json")
|
133
146
|
end
|
@@ -16,6 +16,8 @@ class OriginatorProtocol
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def publish_files files
|
19
|
+
@file_queue = @channel.queue(UUIDTools::UUID.timestamp_create.to_s)
|
20
|
+
|
19
21
|
files.each do |file|
|
20
22
|
@channel.default_exchange.publish(file, :routing_key => @file_queue.name)
|
21
23
|
end
|
@@ -28,6 +30,12 @@ class OriginatorProtocol
|
|
28
30
|
@channel.fanout("gorgon.jobs").publish(job_definition.to_json)
|
29
31
|
end
|
30
32
|
|
33
|
+
def ping_listeners
|
34
|
+
# TODO: we probably want to use a different exchange for pinging when we add more services
|
35
|
+
message = {:type => "ping", :reply_exchange_name => @reply_exchange.name}
|
36
|
+
@channel.fanout("gorgon.jobs").publish(Yajl::Encoder.encode(message))
|
37
|
+
end
|
38
|
+
|
31
39
|
def receive_payloads
|
32
40
|
@reply_queue.subscribe do |payload|
|
33
41
|
yield payload
|
@@ -35,7 +43,7 @@ class OriginatorProtocol
|
|
35
43
|
end
|
36
44
|
|
37
45
|
def cancel_job
|
38
|
-
@file_queue.purge
|
46
|
+
@file_queue.purge if @file_queue
|
39
47
|
@channel.fanout("gorgon.worker_managers").publish(cancel_message)
|
40
48
|
@logger.log "Cancel Message sent"
|
41
49
|
end
|
@@ -51,12 +59,11 @@ class OriginatorProtocol
|
|
51
59
|
@reply_queue = @channel.queue(UUIDTools::UUID.timestamp_create.to_s)
|
52
60
|
@reply_exchange = @channel.direct(UUIDTools::UUID.timestamp_create.to_s)
|
53
61
|
@reply_queue.bind(@reply_exchange)
|
54
|
-
@file_queue = @channel.queue(UUIDTools::UUID.timestamp_create.to_s)
|
55
62
|
end
|
56
63
|
|
57
64
|
def cleanup_queues
|
58
|
-
@reply_queue.delete
|
59
|
-
@file_queue.delete
|
65
|
+
@reply_queue.delete if @reply_queue
|
66
|
+
@file_queue.delete if @file_queue
|
60
67
|
end
|
61
68
|
|
62
69
|
def cancel_message
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'gorgon/originator_protocol'
|
2
|
+
require 'gorgon/configuration'
|
3
|
+
require 'gorgon/originator_logger'
|
4
|
+
require 'gorgon/colors'
|
5
|
+
|
6
|
+
require 'colorize'
|
7
|
+
|
8
|
+
class PingService
|
9
|
+
include Configuration
|
10
|
+
|
11
|
+
TIMEOUT=4
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@configuration = load_configuration_from_file("gorgon.json")
|
15
|
+
@logger = OriginatorLogger.new @configuration[:originator_log_file]
|
16
|
+
@protocol = OriginatorProtocol.new @logger
|
17
|
+
@listeners = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def ping_listeners
|
21
|
+
Signal.trap("INT") { disconnect }
|
22
|
+
Signal.trap("TERM") { disconnect }
|
23
|
+
|
24
|
+
EventMachine.run do
|
25
|
+
@logger.log "Connecting..."
|
26
|
+
@protocol.connect @configuration[:connection], :on_closed => proc {EM.stop}
|
27
|
+
|
28
|
+
@logger.log "Pinging Listeners..."
|
29
|
+
@protocol.ping_listeners
|
30
|
+
|
31
|
+
EM.add_timer(TIMEOUT) { disconnect }
|
32
|
+
|
33
|
+
@protocol.receive_payloads do |payload|
|
34
|
+
@logger.log "Received #{payload}"
|
35
|
+
|
36
|
+
handle_reply(Yajl::Parser.new(:symbolize_keys => true).parse(payload))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def disconnect
|
44
|
+
@protocol.disconnect
|
45
|
+
print_summary
|
46
|
+
end
|
47
|
+
|
48
|
+
def handle_reply payload
|
49
|
+
if payload[:type] != "ping_response"
|
50
|
+
puts "Unexpected message received: #{payload}"
|
51
|
+
return
|
52
|
+
end
|
53
|
+
|
54
|
+
@listeners << payload
|
55
|
+
hostname = payload[:hostname].colorize(Colors::HOST)
|
56
|
+
puts "#{hostname} is running Listener version #{payload[:version]}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def print_summary
|
60
|
+
puts "\n#{@listeners.size} host(s) responded."
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_disconnect
|
64
|
+
EventMachine.stop
|
65
|
+
end
|
66
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'gorgon/colors'
|
2
|
+
|
1
3
|
require 'ruby-progressbar'
|
2
4
|
require 'colorize'
|
3
5
|
|
@@ -6,9 +8,6 @@ LOADING_MSG = "Loading environment and workers..."
|
|
6
8
|
RUNNING_MSG = "Running files:"
|
7
9
|
LEGEND_MSG = "Legend:\nF - failure files count\nH - number of hosts that have run files\nW - number of workers running files"
|
8
10
|
|
9
|
-
FILENAME_COLOR = :light_cyan
|
10
|
-
HOST_COLOR = :light_blue
|
11
|
-
|
12
11
|
class ProgressBarView
|
13
12
|
def initialize job_state
|
14
13
|
@job_state = job_state
|
@@ -28,7 +27,7 @@ class ProgressBarView
|
|
28
27
|
|
29
28
|
failed_files_count = @job_state.failed_files_count
|
30
29
|
|
31
|
-
@progress_bar.title="F: #{failed_files_count} H: #{@job_state.total_running_hosts} W: #{@job_state.total_running_workers}"
|
30
|
+
@progress_bar.title=" F: #{failed_files_count} H: #{@job_state.total_running_hosts} W: #{@job_state.total_running_workers}"
|
32
31
|
if failed_files_count > 0
|
33
32
|
@progress_bar.format(format(bar: :red, title: :default))
|
34
33
|
end
|
@@ -57,7 +56,7 @@ private
|
|
57
56
|
end
|
58
57
|
|
59
58
|
def output_gorgon_crash_message payload
|
60
|
-
$stderr.puts "\nA #{'crash'.red} occured at '#{payload[:hostname].colorize
|
59
|
+
$stderr.puts "\nA #{'crash'.red} occured at '#{payload[:hostname].colorize Colors::HOST}':"
|
61
60
|
$stderr.puts payload[:stdout].yellow unless payload[:stdout].to_s.strip.length == 0
|
62
61
|
$stderr.puts payload[:stderr].yellow unless payload[:stderr].to_s.strip.length == 0
|
63
62
|
if @progress_bar.nil?
|
@@ -71,7 +70,7 @@ private
|
|
71
70
|
bar = "%w>%i".colorize(colors[:bar])
|
72
71
|
title = "%t".colorize(colors[:title])
|
73
72
|
|
74
|
-
"
|
73
|
+
"#{title} | [#{bar}] %c/%C %e"
|
75
74
|
end
|
76
75
|
|
77
76
|
def terminal_size
|
@@ -87,8 +86,8 @@ private
|
|
87
86
|
def print_failed_tests
|
88
87
|
@job_state.each_failed_test do |test|
|
89
88
|
puts "\n" + ('*' * 80).magenta #light_red
|
90
|
-
puts("File '#{test[:filename].colorize(
|
91
|
-
+ "'#{test[:hostname].colorize(
|
89
|
+
puts("File '#{test[:filename].colorize(Colors::FILENAME)}' failed/crashed at " \
|
90
|
+
+ "'#{test[:hostname].colorize(Colors::HOST)}'\n")
|
92
91
|
msg = build_fail_message test[:failures]
|
93
92
|
puts "#{msg}\n"
|
94
93
|
end
|
@@ -112,8 +111,8 @@ private
|
|
112
111
|
puts "\n#{title} - The following files were still running:" if @job_state.total_running_workers > 0
|
113
112
|
|
114
113
|
@job_state.each_running_file do |hostname, filename|
|
115
|
-
filename_str = filename.dup.colorize(
|
116
|
-
hostname_str = hostname.dup.colorize(
|
114
|
+
filename_str = filename.dup.colorize(Colors::FILENAME)
|
115
|
+
hostname_str = hostname.dup.colorize(Colors::HOST)
|
117
116
|
puts "\t#{filename_str} at '#{hostname_str}'"
|
118
117
|
end
|
119
118
|
end
|
@@ -14,6 +14,8 @@ class SourceTreeSyncer
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def sync
|
17
|
+
return if blank_source_tree_path?
|
18
|
+
|
17
19
|
@tempdir = Dir.mktmpdir("gorgon")
|
18
20
|
Dir.chdir(@tempdir)
|
19
21
|
|
@@ -34,11 +36,26 @@ class SourceTreeSyncer
|
|
34
36
|
end
|
35
37
|
|
36
38
|
def remove_temp_dir
|
37
|
-
FileUtils::remove_entry_secure(@tempdir)
|
39
|
+
FileUtils::remove_entry_secure(@tempdir) if @tempdir
|
38
40
|
end
|
39
41
|
|
40
42
|
private
|
41
43
|
|
44
|
+
def blank_source_tree_path?
|
45
|
+
if @source_tree_path.nil?
|
46
|
+
@errors = "Source tree path cannot be nil. Check your gorgon.json file."
|
47
|
+
elsif @source_tree_path.strip.empty?
|
48
|
+
@errors = "Source tree path cannot be empty. Check your gorgon.json file."
|
49
|
+
end
|
50
|
+
|
51
|
+
if @errors
|
52
|
+
@exitstatus = 1
|
53
|
+
return true
|
54
|
+
else
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
42
59
|
def build_exclude_opt
|
43
60
|
return "" if @exclude.nil? or @exclude.empty?
|
44
61
|
|
data/lib/gorgon/version.rb
CHANGED
data/lib/gorgon/worker.rb
CHANGED
@@ -32,8 +32,6 @@ class Worker
|
|
32
32
|
include GLogger
|
33
33
|
|
34
34
|
def self.build(config)
|
35
|
-
Signal.trap("INT") { interrupted }
|
36
|
-
|
37
35
|
payload = Yajl::Parser.new(:symbolize_keys => true).parse($stdin.read)
|
38
36
|
job_definition = JobDefinition.new(payload)
|
39
37
|
|
@@ -77,6 +75,7 @@ class Worker
|
|
77
75
|
@amqp.start_worker @file_queue_name, @reply_exchange_name do |queue, exchange|
|
78
76
|
while filename = queue.pop
|
79
77
|
exchange.publish make_start_message(filename)
|
78
|
+
log "Running '#{filename}'"
|
80
79
|
test_results = run_file(filename)
|
81
80
|
exchange.publish make_finish_message(filename, test_results)
|
82
81
|
end
|
@@ -103,8 +102,4 @@ class Worker
|
|
103
102
|
def make_finish_message(filename, results)
|
104
103
|
{:action => :finish, :hostname => Socket.gethostname, :worker_id => @worker_id, :filename => filename}.merge(results)
|
105
104
|
end
|
106
|
-
|
107
|
-
def self.interrupted
|
108
|
-
exit # to avoid raising "INT" exception
|
109
|
-
end
|
110
105
|
end
|
@@ -19,6 +19,8 @@ class WorkerManager
|
|
19
19
|
|
20
20
|
def initialize config
|
21
21
|
initialize_logger config[:log_file]
|
22
|
+
log "Worker Manager #{Gorgon::VERSION} initializing"
|
23
|
+
|
22
24
|
@worker_pids = []
|
23
25
|
|
24
26
|
@config = config
|
@@ -115,10 +117,15 @@ class WorkerManager
|
|
115
117
|
def on_current_job_complete
|
116
118
|
log "Job '#{@job_definition.inspect}' completed"
|
117
119
|
|
120
|
+
stop
|
121
|
+
end
|
122
|
+
|
123
|
+
def stop
|
118
124
|
EventMachine.stop_event_loop
|
119
125
|
@bunny.stop
|
120
126
|
end
|
121
127
|
|
128
|
+
CANCEL_TIMEOUT = 15
|
122
129
|
def subscribe_to_originator_queue
|
123
130
|
|
124
131
|
originator_watcher = proc do
|
@@ -138,6 +145,8 @@ class WorkerManager
|
|
138
145
|
log "Sending 'INT' signal to #{@worker_pids}"
|
139
146
|
Process.kill("INT", *@worker_pids)
|
140
147
|
log "Signal sent"
|
148
|
+
|
149
|
+
EM.add_timer(CANCEL_TIMEOUT) { stop }
|
141
150
|
else
|
142
151
|
EventMachine.defer(originator_watcher, handle_message)
|
143
152
|
end
|
data/spec/job_definition_spec.rb
CHANGED
@@ -8,8 +8,7 @@ describe JobDefinition do
|
|
8
8
|
|
9
9
|
describe "#to_json" do
|
10
10
|
it "should serialize itself to json" do
|
11
|
-
expected_hash = {:file_queue_name => "string 1", :reply_exchange_name => "string 2",
|
12
|
-
:source_tree_path => "string 3", :sync_exclude => "string 4", :callbacks => {}}
|
11
|
+
expected_hash = {:type => "job_definition", :file_queue_name => "string 1", :reply_exchange_name => "string 2", :source_tree_path => "string 3", :sync_exclude => "string 4", :callbacks => {}}
|
13
12
|
|
14
13
|
jd = JobDefinition.new(expected_hash)
|
15
14
|
|
data/spec/listener_spec.rb
CHANGED
@@ -43,7 +43,7 @@ describe Listener do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should log to 'log_file'" do
|
46
|
-
logger.should_receive(:info).with(
|
46
|
+
logger.should_receive(:info).with(/Listener.*initializing/)
|
47
47
|
|
48
48
|
Listener.new
|
49
49
|
end
|
@@ -98,7 +98,7 @@ describe Listener do
|
|
98
98
|
describe "#poll" do
|
99
99
|
|
100
100
|
let(:empty_queue) { {:payload => :queue_empty} }
|
101
|
-
let(:job_payload) { {:payload => "
|
101
|
+
let(:job_payload) { {:payload => Yajl::Encoder.encode({:type => "job_definition"}) } }
|
102
102
|
before do
|
103
103
|
listener.stub(:run_job)
|
104
104
|
end
|
@@ -125,7 +125,7 @@ describe Listener do
|
|
125
125
|
|
126
126
|
it "starts a new job when there is a job payload" do
|
127
127
|
queue.should_receive(:pop).and_return(job_payload)
|
128
|
-
listener.should_receive(:run_job).with(
|
128
|
+
listener.should_receive(:run_job).with({:type => "job_definition"})
|
129
129
|
listener.poll
|
130
130
|
end
|
131
131
|
|
@@ -133,6 +133,24 @@ describe Listener do
|
|
133
133
|
listener.poll.should be_true
|
134
134
|
end
|
135
135
|
end
|
136
|
+
|
137
|
+
context "ping message pending on queue" do
|
138
|
+
let(:ping_payload) {{
|
139
|
+
:payload => Yajl::Encoder.encode({:type => "ping", :reply_exchange_name => "name"}) }}
|
140
|
+
|
141
|
+
before do
|
142
|
+
queue.stub!(:pop => ping_payload)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "publishes ping_response message with Gorgon's version" do
|
146
|
+
listener.should_not_receive(:run_job)
|
147
|
+
bunny.should_receive(:exchange).with("name", anything).and_return(exchange)
|
148
|
+
response = {:type => "ping_response", :hostname => Socket.gethostname,
|
149
|
+
:version => Gorgon::VERSION}
|
150
|
+
exchange.should_receive(:publish).with(Yajl::Encoder.encode(response))
|
151
|
+
listener.poll
|
152
|
+
end
|
153
|
+
end
|
136
154
|
end
|
137
155
|
|
138
156
|
describe "#run_job" do
|
@@ -153,7 +171,6 @@ describe Listener do
|
|
153
171
|
before do
|
154
172
|
stub_classes
|
155
173
|
@listener = Listener.new
|
156
|
-
@json_payload = Yajl::Encoder.encode(payload)
|
157
174
|
end
|
158
175
|
|
159
176
|
it "copy source tree" do
|
@@ -161,7 +178,7 @@ describe Listener do
|
|
161
178
|
syncer.should_receive(:exclude=).with(["log"])
|
162
179
|
syncer.should_receive(:sync)
|
163
180
|
syncer.should_receive(:success?).and_return(true)
|
164
|
-
@listener.run_job(
|
181
|
+
@listener.run_job(payload)
|
165
182
|
end
|
166
183
|
|
167
184
|
context "syncer#sync fails" do
|
@@ -173,13 +190,13 @@ describe Listener do
|
|
173
190
|
|
174
191
|
it "aborts current job" do
|
175
192
|
callback_handler.should_not_receive(:after_sync)
|
176
|
-
@listener.run_job(
|
193
|
+
@listener.run_job(payload)
|
177
194
|
end
|
178
195
|
|
179
196
|
it "sends message to originator with output and errors from syncer" do
|
180
197
|
reply = {:type => :crash, :hostname => "hostname", :stdout => "some output", :stderr => "some errors"}
|
181
198
|
exchange.should_receive(:publish).with(Yajl::Encoder.encode(reply))
|
182
|
-
@listener.run_job(
|
199
|
+
@listener.run_job(reply)
|
183
200
|
end
|
184
201
|
end
|
185
202
|
|
@@ -193,28 +210,28 @@ describe Listener do
|
|
193
210
|
stderr.should_receive(:read).and_return "some errors"
|
194
211
|
reply = {:type => :crash, :hostname => "hostname", :stdout => "some output", :stderr => "some errors"}
|
195
212
|
exchange.should_receive(:publish).with(Yajl::Encoder.encode(reply))
|
196
|
-
@listener.run_job(
|
213
|
+
@listener.run_job(reply)
|
197
214
|
end
|
198
215
|
end
|
199
216
|
|
200
217
|
it "remove temp source directory when complete" do
|
201
218
|
syncer.should_receive(:remove_temp_dir)
|
202
|
-
@listener.run_job(
|
219
|
+
@listener.run_job(payload)
|
203
220
|
end
|
204
221
|
|
205
222
|
it "creates a CallbackHandler object using callbacks passed in payload" do
|
206
223
|
CallbackHandler.should_receive(:new).once.with({:a_callback => "path/to/callback"}).and_return(callback_handler)
|
207
|
-
@listener.run_job(
|
224
|
+
@listener.run_job(payload)
|
208
225
|
end
|
209
226
|
|
210
227
|
it "calls after_sync callback" do
|
211
228
|
callback_handler.should_receive(:after_sync).once
|
212
|
-
@listener.run_job(
|
229
|
+
@listener.run_job(payload)
|
213
230
|
end
|
214
231
|
|
215
232
|
it "uses Bundler#with_clean_env so the workers load new gems that could have been installed in after_sync" do
|
216
233
|
Bundler.should_receive(:with_clean_env).and_yield
|
217
|
-
@listener.run_job(
|
234
|
+
@listener.run_job(payload)
|
218
235
|
end
|
219
236
|
end
|
220
237
|
|
@@ -39,7 +39,7 @@ describe OriginatorProtocol do
|
|
39
39
|
|
40
40
|
it "opens a reply and exchange queue" do
|
41
41
|
UUIDTools::UUID.stub!(:timestamp_create).and_return 1
|
42
|
-
channel.should_receive(:queue).
|
42
|
+
channel.should_receive(:queue).once.with("1")
|
43
43
|
@originator_p.connect @conn_information
|
44
44
|
end
|
45
45
|
|
@@ -68,6 +68,7 @@ describe OriginatorProtocol do
|
|
68
68
|
describe "#publish_job" do
|
69
69
|
before do
|
70
70
|
@originator_p.connect @conn_information
|
71
|
+
@originator_p.publish_files []
|
71
72
|
end
|
72
73
|
|
73
74
|
it "add queue's names to job_definition and fanout using 'gorgon.jobs' exchange" do
|
@@ -81,6 +82,19 @@ describe OriginatorProtocol do
|
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
85
|
+
describe "#ping_listeners" do
|
86
|
+
before do
|
87
|
+
@originator_p.connect @conn_information
|
88
|
+
end
|
89
|
+
|
90
|
+
it "adds reply_exchange_name to ping_messages and fanouts it using 'gorgon.jobs' exchange" do
|
91
|
+
expected_msg = {:type => "ping", :reply_exchange_name => "exchange"}
|
92
|
+
channel.should_receive(:fanout).once.ordered.with("gorgon.jobs")
|
93
|
+
exchange.should_receive(:publish).once.ordered.with(Yajl::Encoder.encode(expected_msg))
|
94
|
+
@originator_p.ping_listeners
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
84
98
|
describe "#receive_payloads" do
|
85
99
|
before do
|
86
100
|
@originator_p.connect @conn_information
|
@@ -104,6 +118,7 @@ describe OriginatorProtocol do
|
|
104
118
|
end
|
105
119
|
|
106
120
|
it "purges file_queue" do
|
121
|
+
@originator_p.publish_files ['file1']
|
107
122
|
queue.should_receive(:purge)
|
108
123
|
@originator_p.cancel_job
|
109
124
|
end
|
@@ -122,6 +137,7 @@ describe OriginatorProtocol do
|
|
122
137
|
end
|
123
138
|
|
124
139
|
it "deletes reply and file queue" do
|
140
|
+
@originator_p.publish_files []
|
125
141
|
queue.should_receive(:delete).twice
|
126
142
|
@originator_p.disconnect
|
127
143
|
end
|
data/spec/originator_spec.rb
CHANGED
@@ -118,7 +118,8 @@ describe Originator do
|
|
118
118
|
|
119
119
|
it "builds source_tree_path if it was not specified in the configuration" do
|
120
120
|
@originator.stub!(:configuration).and_return({:job => {}})
|
121
|
-
|
121
|
+
UDPSocket.any_instance.stub(:addr).and_return(["1.1.1.1"])
|
122
|
+
@originator.job_definition.source_tree_path.should == "#{Etc.getlogin}@1.1.1.1:#{Dir.pwd}"
|
122
123
|
end
|
123
124
|
|
124
125
|
it "returns source_tree_path specified in configuration if it is present" do
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'gorgon/ping_service'
|
2
|
+
|
3
|
+
describe "PingService" do
|
4
|
+
describe "#ping_listeners" do
|
5
|
+
let(:configuration){ {:connection => {:host => "host"}, :originator_log_file => "file.log"}}
|
6
|
+
let(:protocol) { stub("OriginatorProtocol", :connect => nil, :ping => nil,
|
7
|
+
:receive_payloads => nil, :disconnect => nil,
|
8
|
+
:ping_listeners => nil)}
|
9
|
+
let(:logger){ stub("Originator Logger", :log => nil, :log_message => nil)}
|
10
|
+
|
11
|
+
before do
|
12
|
+
$stdout.stub!(:write)
|
13
|
+
PingService.any_instance.stub(:load_configuration_from_file).and_return configuration
|
14
|
+
EventMachine.stub!(:run).and_yield
|
15
|
+
EM.stub!(:add_timer).and_yield
|
16
|
+
OriginatorLogger.stub!(:new).and_return logger
|
17
|
+
end
|
18
|
+
|
19
|
+
it "connnects and calls OriginatorProtocol#ping_listeners" do
|
20
|
+
OriginatorProtocol.should_receive(:new).once.ordered.and_return(protocol)
|
21
|
+
protocol.should_receive(:connect).once.ordered.with({:host => "host"}, anything)
|
22
|
+
protocol.should_receive(:ping_listeners).once.ordered
|
23
|
+
PingService.new.ping_listeners
|
24
|
+
end
|
25
|
+
|
26
|
+
context "after sending ping messages" do
|
27
|
+
before do
|
28
|
+
OriginatorProtocol.stub!(:new).and_return(protocol)
|
29
|
+
@service = PingService.new
|
30
|
+
end
|
31
|
+
|
32
|
+
it "adds an Event machine timer" do
|
33
|
+
EM.should_receive(:add_timer).and_yield
|
34
|
+
@service.ping_listeners
|
35
|
+
end
|
36
|
+
|
37
|
+
it "receives a ping_response message" do
|
38
|
+
payload = {:type => "ping_response", :hostname => "host", :version => "1.1.1"}
|
39
|
+
protocol.should_receive(:receive_payloads).and_yield Yajl::Encoder.encode(payload)
|
40
|
+
@service.ping_listeners
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -26,6 +26,24 @@ describe SourceTreeSyncer.new("") do
|
|
26
26
|
@syncer.sync
|
27
27
|
end
|
28
28
|
|
29
|
+
context "invalid source_tree_path" do
|
30
|
+
it "gives error if source_tree_path is empty string" do
|
31
|
+
syncer = SourceTreeSyncer.new " "
|
32
|
+
Dir.should_not_receive(:mktmpdir)
|
33
|
+
syncer.sync
|
34
|
+
syncer.success?.should be_false
|
35
|
+
syncer.errors.should == "Source tree path cannot be empty. Check your gorgon.json file."
|
36
|
+
end
|
37
|
+
|
38
|
+
it "gives error if source_tree_path is nil" do
|
39
|
+
syncer = SourceTreeSyncer.new nil
|
40
|
+
Dir.should_not_receive(:mktmpdir)
|
41
|
+
syncer.sync
|
42
|
+
syncer.success?.should be_false
|
43
|
+
syncer.errors.should == "Source tree path cannot be nil. Check your gorgon.json file."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
29
47
|
context "options" do
|
30
48
|
it "runs rsync system command with appropriate options" do
|
31
49
|
cmd = /rsync.*-azr .*path\/to\/source\/\ \./
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gorgon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2012-09-
|
16
|
+
date: 2012-09-26 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: rspec
|
@@ -211,6 +211,7 @@ files:
|
|
211
211
|
- lib/gorgon.rb
|
212
212
|
- lib/gorgon/amqp_service.rb
|
213
213
|
- lib/gorgon/callback_handler.rb
|
214
|
+
- lib/gorgon/colors.rb
|
214
215
|
- lib/gorgon/configuration.rb
|
215
216
|
- lib/gorgon/failures_printer.rb
|
216
217
|
- lib/gorgon/g_logger.rb
|
@@ -222,6 +223,7 @@ files:
|
|
222
223
|
- lib/gorgon/originator.rb
|
223
224
|
- lib/gorgon/originator_logger.rb
|
224
225
|
- lib/gorgon/originator_protocol.rb
|
226
|
+
- lib/gorgon/ping_service.rb
|
225
227
|
- lib/gorgon/pipe_manager.rb
|
226
228
|
- lib/gorgon/progress_bar_view.rb
|
227
229
|
- lib/gorgon/source_tree_syncer.rb
|
@@ -239,6 +241,7 @@ files:
|
|
239
241
|
- spec/originator_logger_spec.rb
|
240
242
|
- spec/originator_protocol_spec.rb
|
241
243
|
- spec/originator_spec.rb
|
244
|
+
- spec/ping_service_spec.rb
|
242
245
|
- spec/progress_bar_view_spec.rb
|
243
246
|
- spec/source_tree_syncer_spec.rb
|
244
247
|
- spec/worker_manager_spec.rb
|