hello_goodbye 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +24 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +308 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/hello_goodbye.gemspec +24 -0
- data/hello_goodbye.rb +25 -0
- data/lib/hello_goodbye/console.rb +88 -0
- data/lib/hello_goodbye/consoles/foreman_console.rb +23 -0
- data/lib/hello_goodbye/consoles/manager_console.rb +35 -0
- data/lib/hello_goodbye/foreman.rb +117 -0
- data/lib/hello_goodbye/foremen_manager.rb +103 -0
- data/lib/hello_goodbye/json/request.rb +20 -0
- data/lib/hello_goodbye/json/response.rb +36 -0
- data/lib/hello_goodbye/version.rb +3 -0
- data/lib/hello_goodbye.rb +25 -0
- data/spec/hello_goodbye/console_spec.rb +67 -0
- data/spec/hello_goodbye/consoles/foreman_console_spec.rb +35 -0
- data/spec/hello_goodbye/consoles/manager_console_spec.rb +47 -0
- data/spec/hello_goodbye/foremen_manager_spec.rb +84 -0
- data/spec/hello_goodbye/foremen_spec.rb +64 -0
- data/spec/hello_goodbye/json/request_spec.rb +28 -0
- data/spec/hello_goodbye/json/response_spec.rb +35 -0
- data/spec/hello_goodbye_spec.rb +21 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +66 -0
- data/spec/test_console.rb +23 -0
- data/spec/test_foreman.rb +15 -0
- metadata +179 -0
data/.gitignore
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# rcov generated
|
2
|
+
coverage
|
3
|
+
|
4
|
+
# rdoc generated
|
5
|
+
rdoc
|
6
|
+
|
7
|
+
# yard generated
|
8
|
+
doc
|
9
|
+
.yardoc
|
10
|
+
|
11
|
+
# bundler
|
12
|
+
.bundle
|
13
|
+
|
14
|
+
# jeweler generated
|
15
|
+
pkg
|
16
|
+
|
17
|
+
# For vim:
|
18
|
+
*.swp
|
19
|
+
|
20
|
+
# built gems
|
21
|
+
*.gem
|
22
|
+
|
23
|
+
vendor/bundle
|
24
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Joshua Murray
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,308 @@
|
|
1
|
+
HelloGoodbye
|
2
|
+
===============
|
3
|
+
A daemon manager with a TCP interface built on top of EventMachine.
|
4
|
+
|
5
|
+
Installation
|
6
|
+
------------------
|
7
|
+
gem build hello_goodbye.gemspec
|
8
|
+
gem install hello_goodbye-0.1.0.gem
|
9
|
+
|
10
|
+
The Foremen Manager
|
11
|
+
-------------------
|
12
|
+
The foremen manager is the mother-ship for all your custom foremen that will spawn workers (or do whatever you wish).
|
13
|
+
A foreman itself, the manager creates a TCP console of its own so foremen can be reviewed and managed from the manager
|
14
|
+
itself.
|
15
|
+
|
16
|
+
manager = HelloGoodbye::ForemenManager.new(:port => 8080, :server => "127.0.0.1")
|
17
|
+
# or...
|
18
|
+
manager = HelloGoodbye.manager(8080, "127.0.0.1")
|
19
|
+
|
20
|
+
To register foremen (before "start!"ing the manager), execute code like the following:
|
21
|
+
|
22
|
+
manager.register_foreman( :port => 8081, :class => HelloGoodbye::TestForeman )
|
23
|
+
|
24
|
+
If you want to capture errors (and you should -- if the manager goes down, everything is liable to go down with it),
|
25
|
+
pass a block to the on_error method as follows:
|
26
|
+
|
27
|
+
manager.on_error do |e|
|
28
|
+
# Do stuff.
|
29
|
+
end
|
30
|
+
|
31
|
+
When you're all set up, fire away with the start! method:
|
32
|
+
|
33
|
+
manager.start!
|
34
|
+
|
35
|
+
This will block. After this is executed, all your manager and all your foremen should be listening in on their respective ports
|
36
|
+
for your command.
|
37
|
+
|
38
|
+
Consoles
|
39
|
+
-------------
|
40
|
+
After making a TCP connection to one of the consoles, communication should occur to and from the console using one of the following standard JSON packages.
|
41
|
+
|
42
|
+
Commands issued to a service should be formatted as follows:
|
43
|
+
|
44
|
+
{
|
45
|
+
"command": "YOUR COMMAND"
|
46
|
+
}
|
47
|
+
|
48
|
+
Responses from the service will be formatted as follows:
|
49
|
+
|
50
|
+
{
|
51
|
+
"success": true,
|
52
|
+
"message": "MESSAGE FROM SERVICE",
|
53
|
+
"results": []
|
54
|
+
}
|
55
|
+
|
56
|
+
Note the following:
|
57
|
+
|
58
|
+
* **success** can be **true** for **false**
|
59
|
+
* **results** will either be an array of data relavent to the command, or will be absent.
|
60
|
+
|
61
|
+
The Manager Console
|
62
|
+
--------------------
|
63
|
+
Once started, your manager will be available for TCP connections, and will respond to the following commands:
|
64
|
+
<table>
|
65
|
+
<tr>
|
66
|
+
<th>Command</th><th>Response Message</th><th>Results</th><th>Action Performed</th>
|
67
|
+
</tr>
|
68
|
+
<tr>
|
69
|
+
<td>
|
70
|
+
hello
|
71
|
+
</td>
|
72
|
+
<td>
|
73
|
+
hello
|
74
|
+
</td>
|
75
|
+
<td>
|
76
|
+
None.
|
77
|
+
</td>
|
78
|
+
<td>
|
79
|
+
Nothing. Just a convenient way to "ping" the service.
|
80
|
+
</td>
|
81
|
+
</tr>
|
82
|
+
<tr>
|
83
|
+
<td>
|
84
|
+
goodbye
|
85
|
+
</td>
|
86
|
+
<td>
|
87
|
+
goodbye
|
88
|
+
</td>
|
89
|
+
<td>
|
90
|
+
None.
|
91
|
+
</td>
|
92
|
+
<td>
|
93
|
+
Closes the connection.
|
94
|
+
</td>
|
95
|
+
</tr>
|
96
|
+
<tr>
|
97
|
+
<td>
|
98
|
+
foremen
|
99
|
+
</td>
|
100
|
+
<td>
|
101
|
+
ok
|
102
|
+
</td>
|
103
|
+
<td>
|
104
|
+
An array of hashes with details about each forman.
|
105
|
+
</td>
|
106
|
+
<td>
|
107
|
+
Nothing.
|
108
|
+
</td>
|
109
|
+
</tr>
|
110
|
+
<tr>
|
111
|
+
<td>
|
112
|
+
start XX
|
113
|
+
</td>
|
114
|
+
<td>
|
115
|
+
"ok" if successful.
|
116
|
+
</td>
|
117
|
+
<td>
|
118
|
+
An array of started foreman ids.
|
119
|
+
</td>
|
120
|
+
<td>
|
121
|
+
The foreman with the name XX is started. If XX is "all", all stopped foremen
|
122
|
+
will be started.
|
123
|
+
|
124
|
+
XX will be "test" if the foreman name is TestForman.
|
125
|
+
Otherwise, if XX is an integer, the ID of the active foreman will be consulted
|
126
|
+
instead of the name.
|
127
|
+
</td>
|
128
|
+
</tr>
|
129
|
+
<tr>
|
130
|
+
<td>
|
131
|
+
stop XX
|
132
|
+
</td>
|
133
|
+
<td>
|
134
|
+
"ok" if successful.
|
135
|
+
</td>
|
136
|
+
<td>
|
137
|
+
An array of stopped foreman ids.
|
138
|
+
</td>
|
139
|
+
<td>
|
140
|
+
The foreman with the name XX is stopped. If XX is "all", all active foremen
|
141
|
+
will be stopped.
|
142
|
+
|
143
|
+
XX will be "test" if the foreman name is TestForman.
|
144
|
+
Otherwise, if XX is an integer, the ID of the active foreman will be consulted
|
145
|
+
instead of the name.
|
146
|
+
</td>
|
147
|
+
</tr>
|
148
|
+
<tr>
|
149
|
+
<td>
|
150
|
+
(Anything else)
|
151
|
+
</td>
|
152
|
+
<td>
|
153
|
+
unknown command
|
154
|
+
</td>
|
155
|
+
<td>
|
156
|
+
None.
|
157
|
+
</td>
|
158
|
+
<td>Nothing.</td>
|
159
|
+
</tr>
|
160
|
+
</table>
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
Custom Foremen
|
165
|
+
-------------------
|
166
|
+
Foremen that you build must inherit from HelloGoodbye::Foreman. Beyond that, you should only have to implement a few instance methods that will be executed when the corresponding console commands are executed during a TCP connection:
|
167
|
+
|
168
|
+
def start
|
169
|
+
# Start listening for events to respond to.
|
170
|
+
end
|
171
|
+
|
172
|
+
def stop
|
173
|
+
# Stop listening for events.
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
Foremen Console
|
178
|
+
-------------------
|
179
|
+
Once started, your foreman will be available for TCP connections, and will respond to the following commands:
|
180
|
+
<table>
|
181
|
+
<tr>
|
182
|
+
<th>Command</th><th>Response Message</th><th>Results</th><th>Action Performed</th>
|
183
|
+
</tr>
|
184
|
+
<tr>
|
185
|
+
<td>
|
186
|
+
hello
|
187
|
+
</td>
|
188
|
+
<td>
|
189
|
+
hello
|
190
|
+
</td>
|
191
|
+
<td>
|
192
|
+
None.
|
193
|
+
</td>
|
194
|
+
<td>
|
195
|
+
Nothing. Just a convenient way to "ping" the service.
|
196
|
+
</td>
|
197
|
+
</tr>
|
198
|
+
<tr>
|
199
|
+
<td>
|
200
|
+
goodbye
|
201
|
+
</td>
|
202
|
+
<td>
|
203
|
+
goodbye
|
204
|
+
</td>
|
205
|
+
<td>
|
206
|
+
None.
|
207
|
+
</td>
|
208
|
+
<td>
|
209
|
+
Closes the connection.
|
210
|
+
</td>
|
211
|
+
</tr>
|
212
|
+
<tr>
|
213
|
+
<td>
|
214
|
+
start
|
215
|
+
</td>
|
216
|
+
<td>
|
217
|
+
ok
|
218
|
+
</td>
|
219
|
+
<td>
|
220
|
+
Nothing.
|
221
|
+
</td>
|
222
|
+
<td>
|
223
|
+
Executes the foreman's static "start" method. Typically, this would execute whatever "daemon" will listen for events and spawn workers.
|
224
|
+
</td>
|
225
|
+
</tr>
|
226
|
+
<tr>
|
227
|
+
<td>
|
228
|
+
stop
|
229
|
+
</td>
|
230
|
+
<td>
|
231
|
+
ok
|
232
|
+
</td>
|
233
|
+
<td>
|
234
|
+
Nada
|
235
|
+
</td>
|
236
|
+
<td>
|
237
|
+
Executes the forman's "stop" method, stopping the foreman's daemon.
|
238
|
+
</td>
|
239
|
+
</tr>
|
240
|
+
<tr>
|
241
|
+
<td>
|
242
|
+
status
|
243
|
+
</td>
|
244
|
+
<td>
|
245
|
+
"running" or "stopped"
|
246
|
+
</td>
|
247
|
+
<td>
|
248
|
+
Nada
|
249
|
+
</td>
|
250
|
+
<td>
|
251
|
+
Nothing.
|
252
|
+
</td>
|
253
|
+
</tr>
|
254
|
+
<tr>
|
255
|
+
<td>
|
256
|
+
(Anything else not implemented by your custom foreman)
|
257
|
+
</td>
|
258
|
+
<td>
|
259
|
+
unknown command
|
260
|
+
</td>
|
261
|
+
<td>
|
262
|
+
None.
|
263
|
+
</td>
|
264
|
+
<td>
|
265
|
+
Nothing.
|
266
|
+
</td>
|
267
|
+
</tr>
|
268
|
+
</table>
|
269
|
+
|
270
|
+
Custom Consoles
|
271
|
+
------------------
|
272
|
+
Although there is a generic
|
273
|
+
```
|
274
|
+
HelloGoodbye::ForemanConsole
|
275
|
+
```
|
276
|
+
that will hopefully suit the needs for most usecases, custom consoles can easily be created and attached to custom foremen as needed.
|
277
|
+
|
278
|
+
First, to implement your custom console, inherit from
|
279
|
+
```
|
280
|
+
HelloGoodbye::ForemanConsole
|
281
|
+
```
|
282
|
+
and override
|
283
|
+
```
|
284
|
+
receive_command
|
285
|
+
```
|
286
|
+
.
|
287
|
+
|
288
|
+
To make your own class extensible, and to make use of the built in console commands implemented in the
|
289
|
+
```
|
290
|
+
HelloGoodbye::ForemanConsole
|
291
|
+
```
|
292
|
+
class, you'll want to start with the template below when overriding this method:
|
293
|
+
|
294
|
+
def receive_command(command)
|
295
|
+
# Process additional commands here.
|
296
|
+
# Return if processes successfully
|
297
|
+
super # Process the default commands. If no match, a failure response will be returned.
|
298
|
+
end
|
299
|
+
|
300
|
+
The last little catch is this: you must let your custom forman class know which console to use. To do this, in your Foreman class, assuming your console class is HelloGoodbye::TestConsole (yes, your console **must** be in the HelloGoodbye module ):
|
301
|
+
|
302
|
+
set_console_type :test # i.e. ":test" for TestConsole
|
303
|
+
|
304
|
+
Thus, the rules are as follow:
|
305
|
+
|
306
|
+
* Your console's class name must be prefixed with "Console".
|
307
|
+
* When setting this type with the class method, you must pass in a symbol matching the de-classified class name, minus the "Console" prefix.
|
308
|
+
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/hello_goodbye/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Joshua Murray"]
|
6
|
+
gem.email = ["joshua.murray@gmail.com"]
|
7
|
+
gem.description = %q{A daemon manager with a TCP interface built on top of EventMachine.}
|
8
|
+
gem.summary = %q{A daemon manager with a TCP interface built on top of EventMachine.}
|
9
|
+
gem.homepage = "https://github.com/joshcom/hello_goodbye"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "hello_goodbye"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = HelloGoodbye::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "eventmachine", "0.12.10"
|
19
|
+
gem.add_dependency "json", "1.5.3"
|
20
|
+
gem.add_development_dependency "bundler"
|
21
|
+
gem.add_development_dependency "rake"
|
22
|
+
gem.add_development_dependency "shoulda", ">= 0"
|
23
|
+
gem.add_development_dependency "rspec", "2.6.0"
|
24
|
+
end
|
data/hello_goodbye.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require File.expand_path('../hello_goodbye/foremen_manager',__FILE__)
|
6
|
+
|
7
|
+
module HelloGoodbye
|
8
|
+
|
9
|
+
# Resets the current foremen manager, so that the next time
|
10
|
+
# self.manager is called, a new ForemenManager instance will be
|
11
|
+
# created.
|
12
|
+
def self.reset!
|
13
|
+
@manager = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
# Create a new manager or use the existing manager. If an existing manager exists,
|
17
|
+
# port and server will be ignored.
|
18
|
+
# Parameters:
|
19
|
+
# * port: The port the manager should connect to.
|
20
|
+
# * server: The server the service will run from.
|
21
|
+
def self.manager(port=ForemenManager.default_manager_port,server=Foreman.default_server)
|
22
|
+
@manager ||= ForemenManager.new(:port => port, :server => server)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module HelloGoodbye
|
2
|
+
|
3
|
+
# Commands shared by all consoles:
|
4
|
+
# * 'hello' => Used to ping the server.
|
5
|
+
# Responds with 'hello'.
|
6
|
+
# * 'goodbye' => Used to close a connection.
|
7
|
+
# Responds with 'goodbye'.
|
8
|
+
class Console < EventMachine::Connection
|
9
|
+
|
10
|
+
require File.expand_path('../../hello_goodbye/consoles/foreman_console',__FILE__)
|
11
|
+
require File.expand_path('../../hello_goodbye/consoles/manager_console',__FILE__)
|
12
|
+
require File.expand_path('../../hello_goodbye/json/request',__FILE__)
|
13
|
+
require File.expand_path('../../hello_goodbye/json/response',__FILE__)
|
14
|
+
|
15
|
+
# :foreman
|
16
|
+
# A reference to the Foreman object that instantiated the console, so that
|
17
|
+
# the console can serve as an interface to this object.
|
18
|
+
attr_accessor :foreman
|
19
|
+
|
20
|
+
# Returns a standard console of a given type. I'm aware this logic is dumb.
|
21
|
+
# Parameters:
|
22
|
+
# * type
|
23
|
+
# ** :manager => ManagerConsole
|
24
|
+
def self.get(type)
|
25
|
+
case type
|
26
|
+
when :manager
|
27
|
+
ManagerConsole
|
28
|
+
when :foreman
|
29
|
+
ForemanConsole
|
30
|
+
else
|
31
|
+
if (obj = HelloGoodbye.const_get("#{type}_console".split('_').collect(&:capitalize).join))
|
32
|
+
obj
|
33
|
+
else
|
34
|
+
raise ArgumentError, "What type of console is #{type}?"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def foreman=(f)
|
40
|
+
f.console = self
|
41
|
+
@foreman = f
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns:
|
45
|
+
# true if data was handled, false if not.
|
46
|
+
def receive_command(command)
|
47
|
+
case command
|
48
|
+
when "hello"
|
49
|
+
send_response :success => true,
|
50
|
+
:message => "hello"
|
51
|
+
return true
|
52
|
+
when "goodbye"
|
53
|
+
send_response :success => true,
|
54
|
+
:message => "goodbye"
|
55
|
+
close_connection
|
56
|
+
return true
|
57
|
+
else
|
58
|
+
send_response :success => false,
|
59
|
+
:message => "unknown command"
|
60
|
+
end
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# Parameters:
|
65
|
+
# hash
|
66
|
+
# * success => true or false
|
67
|
+
# * message => "Your message"
|
68
|
+
# * results => []
|
69
|
+
def send_response(hash)
|
70
|
+
send_data Response.new(hash).to_json
|
71
|
+
end
|
72
|
+
|
73
|
+
def receive_data(data)
|
74
|
+
data = data.strip
|
75
|
+
return false if data == ""
|
76
|
+
|
77
|
+
begin
|
78
|
+
json = Request.new(data)
|
79
|
+
rescue JSON::ParserError => e
|
80
|
+
send_response :success => false,
|
81
|
+
:message => "invalid json"
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
|
85
|
+
receive_command(json.command)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HelloGoodbye
|
2
|
+
class ForemanConsole < Console
|
3
|
+
def receive_command(command)
|
4
|
+
case command
|
5
|
+
when "start"
|
6
|
+
self.foreman.employ
|
7
|
+
send_response :success => true,
|
8
|
+
:message => "ok"
|
9
|
+
return true
|
10
|
+
when "stop"
|
11
|
+
self.foreman.unemploy
|
12
|
+
send_response :success => true,
|
13
|
+
:message => "ok"
|
14
|
+
return true
|
15
|
+
when "status"
|
16
|
+
send_response :success => true,
|
17
|
+
:message => self.foreman.status.to_s
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module HelloGoodbye
|
2
|
+
class ManagerConsole < Console
|
3
|
+
def receive_command(command)
|
4
|
+
case command
|
5
|
+
when /^start /
|
6
|
+
id = command.gsub(/^start /, "")
|
7
|
+
if (started_foremen = self.foreman.trigger_foreman(:start, id))
|
8
|
+
send_response :success => true,
|
9
|
+
:message => "ok",
|
10
|
+
:results => started_foremen
|
11
|
+
else
|
12
|
+
send_response :success => false,
|
13
|
+
:message => "no match for foreman '#{id}'"
|
14
|
+
end
|
15
|
+
return true
|
16
|
+
when /^stop /
|
17
|
+
id = command.gsub(/^stop /, "")
|
18
|
+
if (stopped_foremen = self.foreman.trigger_foreman(:stop,id))
|
19
|
+
send_response :success => true,
|
20
|
+
:message => "ok",
|
21
|
+
:results => stopped_foremen
|
22
|
+
else
|
23
|
+
send_response :success => false,
|
24
|
+
:message => "no match for foreman '#{id}'"
|
25
|
+
end
|
26
|
+
return true
|
27
|
+
when "foremen"
|
28
|
+
send_response :success => true, :message => "ok",
|
29
|
+
:results => self.foreman.report
|
30
|
+
return true
|
31
|
+
end
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module HelloGoodbye
|
2
|
+
class Foreman
|
3
|
+
|
4
|
+
attr_accessor :server, :port, :console, :my_id, :server_id
|
5
|
+
attr_reader :foreman_started
|
6
|
+
|
7
|
+
DEFAULT_SERVER = "127.0.0.1"
|
8
|
+
|
9
|
+
# Overrides the default ForemanConsole console type
|
10
|
+
# to fire up when #start! is called.
|
11
|
+
def self.set_console_type(console_sym)
|
12
|
+
@console_type = console_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the current console type for this class.
|
16
|
+
def self.console_type
|
17
|
+
@console_type ||= :foreman
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.default_server
|
21
|
+
DEFAULT_SERVER
|
22
|
+
end
|
23
|
+
|
24
|
+
# Parameters:
|
25
|
+
# options:
|
26
|
+
# * server =>
|
27
|
+
# * port => The port to run the server on
|
28
|
+
def initialize(options={})
|
29
|
+
self.server = options[:server]
|
30
|
+
self.port = options[:port]
|
31
|
+
@foreman_started = false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Starts the foreman's worker spawning action.
|
35
|
+
def start
|
36
|
+
raise ArgumentError, "Foreman.start must be implemented by child class."
|
37
|
+
end
|
38
|
+
|
39
|
+
# Stops the foreman's worker spawning action.
|
40
|
+
def stop
|
41
|
+
raise ArgumentError, "Foreman.start must be implemented by child class."
|
42
|
+
end
|
43
|
+
|
44
|
+
# Starts the console for the foreman. Subclasses should implement this method,
|
45
|
+
# passing a block to super to start up any tasks (AMQB subscriber, etc)
|
46
|
+
# that may need to be done.
|
47
|
+
def start!
|
48
|
+
raise RuntimeError, "Foreman already started!" if @foreman_started == true
|
49
|
+
start_with_reactor do
|
50
|
+
self.start_console
|
51
|
+
yield if block_given?
|
52
|
+
end
|
53
|
+
@foreman_started = true
|
54
|
+
end
|
55
|
+
|
56
|
+
# Detects the name to report this foreman class as.
|
57
|
+
# For example, will be "test" for "HelloGoodbye::TestForeman".
|
58
|
+
def my_name
|
59
|
+
begin
|
60
|
+
self.class.name.match(/^HelloGoodbye::(.*)Foreman$/)[1].downcase
|
61
|
+
rescue
|
62
|
+
self.class.name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Reports the current status of the foreman.
|
67
|
+
# Returns:
|
68
|
+
# * :stopped if the foreman is not currently employing workers.
|
69
|
+
# * :running if the foreman is active
|
70
|
+
def status
|
71
|
+
@status || :stopped
|
72
|
+
end
|
73
|
+
|
74
|
+
# true if the foreman is currently employing workers
|
75
|
+
def running?
|
76
|
+
self.status == :running
|
77
|
+
end
|
78
|
+
|
79
|
+
# Sets the foreman status to either :running or :stopped
|
80
|
+
def status=(status)
|
81
|
+
@status = status.to_sym
|
82
|
+
end
|
83
|
+
|
84
|
+
# Sets the foreman status to :running and calls self.start
|
85
|
+
def employ
|
86
|
+
self.start
|
87
|
+
self.status = :running
|
88
|
+
end
|
89
|
+
|
90
|
+
# Sets the foreman status to :stopped and calls self.stop
|
91
|
+
def unemploy
|
92
|
+
self.stop
|
93
|
+
self.status = :stopped
|
94
|
+
end
|
95
|
+
|
96
|
+
def server
|
97
|
+
@server || DEFAULT_SERVER
|
98
|
+
end
|
99
|
+
|
100
|
+
def start_console
|
101
|
+
me = self
|
102
|
+
self.server_id = EM::start_server(self.server, self.port, Console.get(self.class.console_type)) do |c|
|
103
|
+
c.foreman = me
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def start_with_reactor(&block)
|
108
|
+
if EM.reactor_running?
|
109
|
+
block.call
|
110
|
+
else
|
111
|
+
EM.run {
|
112
|
+
block.call
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|