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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gorgon (0.1.1)
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.4)
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.2)
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
@@ -0,0 +1,4 @@
1
+ module Colors
2
+ FILENAME = :light_cyan
3
+ HOST = :light_blue
4
+ end
@@ -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
@@ -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} initialized"
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
- run_job(message[:payload])
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 run_job(json_payload)
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
@@ -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}@#{Socket.gethostname}:#{Dir.pwd}"
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 HOST_COLOR}':"
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
- "%e [#{bar}] %c/%C | #{title}"
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(FILENAME_COLOR)}' failed/crashed at " \
91
- + "'#{test[:hostname].colorize(HOST_COLOR)}'\n")
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(FILENAME_COLOR)
116
- hostname_str = hostname.dup.colorize(HOST_COLOR)
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
 
@@ -1,3 +1,3 @@
1
1
  module Gorgon
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
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
@@ -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
 
@@ -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("Listener initialized")
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 => "Job"} }
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(job_payload[:payload])
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(@json_payload)
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(@json_payload)
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(@json_payload)
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(@json_payload)
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(@json_payload)
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(@json_payload)
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(@json_payload)
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(@json_payload)
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).twice.with("1")
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
@@ -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
- @originator.job_definition.source_tree_path.should == "#{Etc.getlogin}@#{Socket.gethostname}:#{Dir.pwd}"
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.1.1
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-24 00:00:00.000000000 Z
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