gorgon 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|