jerbil 1.2.2
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/Bugs.rdoc +66 -0
- data/Gemfile +12 -0
- data/History.txt +359 -0
- data/Intro.txt +5 -0
- data/LICENCE.rdoc +159 -0
- data/README.md +335 -0
- data/README_SERVICES.md +410 -0
- data/README_TESTING.md +47 -0
- data/bin/jerbil +62 -0
- data/bin/jerbil-install +56 -0
- data/etc/conf.d/jerbild +15 -0
- data/etc/conf.d/jserviced +39 -0
- data/etc/init.d/jerbild +55 -0
- data/etc/init.d/jserviced +59 -0
- data/etc/jerbil/jerbil-client.rb +2 -0
- data/etc/jerbil/jerbil.rb +83 -0
- data/lib/jerbil.rb +636 -0
- data/lib/jerbil/config.md +49 -0
- data/lib/jerbil/config.rb +67 -0
- data/lib/jerbil/errors.rb +74 -0
- data/lib/jerbil/jerbil_service/base.rb +191 -0
- data/lib/jerbil/jerbil_service/client.rb +325 -0
- data/lib/jerbil/jerbil_service/config.md +119 -0
- data/lib/jerbil/jerbil_service/config.rb +72 -0
- data/lib/jerbil/jerbil_service/sclient.rb +343 -0
- data/lib/jerbil/jerbil_service/support.rb +58 -0
- data/lib/jerbil/jerbil_service/utils.rb +35 -0
- data/lib/jerbil/servers.rb +230 -0
- data/lib/jerbil/service.rb +216 -0
- data/lib/jerbil/support.rb +160 -0
- data/lib/jerbil/thor/server.rb +76 -0
- data/lib/jerbil/thor/service.rb +74 -0
- data/lib/jerbil/version.rb +13 -0
- data/sbin/jerbil-status +120 -0
- data/sbin/jerbil-stop +139 -0
- data/sbin/jerbild +186 -0
- data/sbin/jservice-status +107 -0
- data/sbin/jservice-stop +94 -0
- data/sbin/jserviced +111 -0
- data/spec/jerbil_2_jerbil_spec.rb +87 -0
- data/spec/jerbil_client1_spec.rb +80 -0
- data/spec/jerbil_client_spec.rb +114 -0
- data/spec/jerbil_client_stop_spec.rb +24 -0
- data/spec/jerbil_daemonised/jerbil_local_spec.rb +81 -0
- data/spec/jerbil_daemonised/jerbil_remote_spec.rb +116 -0
- data/spec/jerbil_load.rb +48 -0
- data/spec/jerbil_local_spec.rb +91 -0
- data/spec/jerbil_missing_spec.rb +98 -0
- data/spec/jerbil_remote_spec.rb +117 -0
- data/spec/jerbil_remote_spec_bup.rb +168 -0
- data/spec/jerbil_service_error_spec.rb +56 -0
- data/spec/jerbil_service_spec.rb +41 -0
- data/spec/jerbil_support_spec.rb +69 -0
- data/spec/jservice_utils_spec.rb +38 -0
- data/spec/server_spec.rb +69 -0
- data/spec/server_update_spec.rb +28 -0
- data/spec/service_spec.rb +72 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test_env_spec.rb +53 -0
- data/test/bad_test_service.rb +31 -0
- data/test/conf.d/jerbil +36 -0
- data/test/conf.d/jerbil.conf +39 -0
- data/test/conf.d/jerbil.rb +55 -0
- data/test/conf.d/jerbil_local.rb +33 -0
- data/test/conf.d/jerbil_no_local.conf +39 -0
- data/test/conf.d/jerbil_old.rb +47 -0
- data/test/conf.d/jerbil_test.rb +35 -0
- data/test/conf.d/malformed +1 -0
- data/test/conf.d/missing_services +39 -0
- data/test/conf.d/ruby_test.rb +8 -0
- data/test/init.d/jerbild +14 -0
- data/test/jerbil.rb +51 -0
- data/test/jerbil_config.rb +8 -0
- data/test/jstop.rb +36 -0
- data/test/key.asc +1 -0
- data/test/lib/ruby_test.rb +37 -0
- data/test/lib/ruby_test/config.rb +56 -0
- data/test/lib/ruby_test/version.rb +13 -0
- data/test/pids/jerbil-prod.asc +1 -0
- data/test/pids/jerbil-prod.pid +1 -0
- data/test/pids/jerbil.pid +1 -0
- data/test/private_key_file.asc +3 -0
- data/test/service-stop.rb +86 -0
- data/test/service_mock.rb +94 -0
- data/test/test_service_client.rb +25 -0
- metadata +265 -0
data/README.md
ADDED
@@ -0,0 +1,335 @@
|
|
1
|
+
# Jerbil
|
2
|
+
|
3
|
+
**(a.k.a Jumpin Ermin's Reliable Broker for Integrated Linux services)**
|
4
|
+
|
5
|
+
An Object Broker for ruby that provides reliable access to services across the LAN
|
6
|
+
and comes complete with a service class that makes writing services easy, hiding all
|
7
|
+
of the broker interactions.
|
8
|
+
|
9
|
+
**GitHub:** [https://github.com/osburn-sharp/jerbil](https://github.com/osburn-sharp/jerbil)
|
10
|
+
|
11
|
+
**RubyDoc:** [http://rubydoc.info/github/osburn-sharp/jerbil/frames](http://rubydoc.info/github/osburn-sharp/jerbil/frames)
|
12
|
+
|
13
|
+
**RubyGems:** [https://rubygems.org/gems/jerbil](https://rubygems.org/gems/jerbil)
|
14
|
+
|
15
|
+
See Also:
|
16
|
+
|
17
|
+
* {file:README_SERVICES.md Services Readme} for details about how to create a service that
|
18
|
+
uses the Jerbil Server
|
19
|
+
* {file:README_TESTING.md Testing Readme} for a note about testing Jerbil and Jerbil Services
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
### Note
|
24
|
+
|
25
|
+
Jerbil will work on any Linux distro (and possibly other systems too) but it comes with
|
26
|
+
all the gear necessary to work on Gentoo out of the box - e.g. runscripts, /etc/conf.d files
|
27
|
+
and the like.
|
28
|
+
|
29
|
+
### Installing the Gem
|
30
|
+
|
31
|
+
Jerbil is available as a gem, so to get the gem:
|
32
|
+
|
33
|
+
# gem install jerbil
|
34
|
+
|
35
|
+
Jerbil needs extra files to be installed on the system, which is done with the help
|
36
|
+
of [Jeni](https://github.com/osburn-sharp/jeni):
|
37
|
+
|
38
|
+
# jerbil-install
|
39
|
+
|
40
|
+
This will install various files over and above those installed by the gem, including configuration files,
|
41
|
+
runscripts, and sbin wrappers for the jerbil server and for services that use the server.
|
42
|
+
Use the -p switch to pretend and see if everything works OK. Jerbil assumes there is a
|
43
|
+
user 'jerbil' that will run the server process etc, so if there is no user 'jerbil' then
|
44
|
+
this script will create one.
|
45
|
+
|
46
|
+
Jerbil can also be installed from source by downloading from GitHub (see above).
|
47
|
+
|
48
|
+
### System Architecture Overview
|
49
|
+
|
50
|
+
A Jerbil Server should be run on every machine that uses or provides services through
|
51
|
+
Jerbil. The servers self-discover each other and share service records so that the loss
|
52
|
+
of a single node in the network does not disrupt the rest of the network. The system is
|
53
|
+
not secure, but all servers share a secret key and check for this key before accepting
|
54
|
+
other servers into the network. Its more to avoid mistakes than proper security.
|
55
|
+
|
56
|
+
Services always register with their local server and clients always request
|
57
|
+
services from their local server. If services inherit the {JerbilService::Base} class all server interactions
|
58
|
+
are hidden from the user and if clients use the {JerbilService::Client} class then all server interactions
|
59
|
+
are also hidden. Service startup and shutdown can be achieved through a common script making it easy to
|
60
|
+
bring a new service into operation.
|
61
|
+
|
62
|
+
All services (and servers) can be run in one of three environments: prod, test and dev. This allows
|
63
|
+
development versions to run in parallel with production versions etc.
|
64
|
+
|
65
|
+
### Configuration
|
66
|
+
|
67
|
+
The configuration file for the server (jerbil.rb) will be placed in a directory /etc/jerbil. As well as checking
|
68
|
+
the defaults, you need to generate a secret. A random secret can be generated using the jerbil command:
|
69
|
+
|
70
|
+
# jerbil server secret
|
71
|
+
|
72
|
+
although any long string will do. Full details of the configuration parameters for Jerbil are in {Jerbil::Config}.
|
73
|
+
|
74
|
+
### Editting /etc/services
|
75
|
+
|
76
|
+
Jerbil uses /etc/services to find its services (unsurprisingly) so this file needs to be updated. There must
|
77
|
+
be an entry for the server itself, as well as the services that use jerbil. Because Jerbil supports three
|
78
|
+
operating environments (dev, test and prod), these services must be assigned in triplets. The following is an example
|
79
|
+
|
80
|
+
# starts from 49195
|
81
|
+
#
|
82
|
+
jerbil 49200/tcp # Jerbil Server
|
83
|
+
jerbil-test 49201/tcp
|
84
|
+
jerbil-dev 49202/tcp
|
85
|
+
|
86
|
+
jexten 49203/tcp # Jexten Service - controls CM15pro
|
87
|
+
jexten-test 49204/tcp
|
88
|
+
jexten-dev 49205/tcp
|
89
|
+
|
90
|
+
tittle 49206/tcp # Tittle - Thermostatic Temperature Logger
|
91
|
+
tittle-test 49207/tcp
|
92
|
+
tittle-dev 49208/tcp
|
93
|
+
|
94
|
+
tittledm 49209/tcp # Tittle's little device manager service
|
95
|
+
tittledm-test 49210/tcp
|
96
|
+
tittledm-dev 49211/tcp
|
97
|
+
|
98
|
+
jemb 49212/tcp # Jumpin' Ermin's Music Box
|
99
|
+
jemb-test 49213/tcp
|
100
|
+
jemb-dev 49214/tcp
|
101
|
+
|
102
|
+
Strictly speaking the intermediate assignments do not need to be defined: Jerbil looks for the first one
|
103
|
+
and offsets it if either test or dev is selected. They are included here as a reminder that the ports may be used.
|
104
|
+
|
105
|
+
### Starting and Stopping the Server
|
106
|
+
|
107
|
+
Once you have generated a secret and set up some services in /etc/services, it is now possible to
|
108
|
+
start the Jerbil Server. This can be done using the jerbild script:
|
109
|
+
|
110
|
+
# /usr/sbin/jerbild
|
111
|
+
|
112
|
+
which will start the server, get the default config file (/etc/jermine/jerbil.rb), daemonise, and say very little.
|
113
|
+
If you add the -h or --help switch you can see how to overide this default behaviour.
|
114
|
+
You can then test that the server is running with:
|
115
|
+
|
116
|
+
# jerbil server
|
117
|
+
|
118
|
+
If this does not report the server as up, go to the troubleshooting section.
|
119
|
+
|
120
|
+
If you can support init scripts, then jerbil is ready to go. Edit /etc/conf.d/jerbild if you need to change settings such as
|
121
|
+
where the config file is and then run:
|
122
|
+
|
123
|
+
# /etc/init.d/jerbild start
|
124
|
+
# rc-update add jerbild default
|
125
|
+
|
126
|
+
the latter command works on Gentoo at least.
|
127
|
+
|
128
|
+
You can also check if the server is working using:
|
129
|
+
|
130
|
+
# /usr/sbin/jerbil-status -V
|
131
|
+
or
|
132
|
+
# /etc/init.d/jerbild status
|
133
|
+
|
134
|
+
To stop the server:
|
135
|
+
|
136
|
+
# /usr/sbin/jerbil-stop
|
137
|
+
or
|
138
|
+
# /etc/init.d/jerbild stop
|
139
|
+
|
140
|
+
Further details of all these commands can be obtained with the -h or --help option
|
141
|
+
|
142
|
+
|
143
|
+
## General Description
|
144
|
+
|
145
|
+
The function of Jerbil is to make it easy to write linux services in ruby that can be deployed anywhere on
|
146
|
+
a network, once or multiple times, and enabling clients to access these services regardless of where they are. Its
|
147
|
+
a wrapper around DRb and a replacement for Rinda.
|
148
|
+
|
149
|
+
A Jerbil Server is required to run on each machine where services are needed or from which they are accessed.
|
150
|
+
Each server will discover other servers on the network and register with them, provided they all share the same
|
151
|
+
secret and are running in the same environment. During registration, new servers will receive details from the other
|
152
|
+
servers of all the services that are local to them. This provides a robust and relatively self-healing network where
|
153
|
+
a server can come and go without needing to restart any of the others. By comparison, the Rinda server presents a
|
154
|
+
single point of failure. If it goes down nobody can find anything and all services have to be restarted when the
|
155
|
+
server is restored.
|
156
|
+
|
157
|
+
Part of the self-healing in Jerbil is dealing with services that become unavailable. When this happens, the server
|
158
|
+
checks if the services local server is running. If it is, this local server is asked to deal with the missing service
|
159
|
+
and update the database accordingly. If it is not, then the original server takes responsibility for the missing
|
160
|
+
service and purges it from all the other servers.
|
161
|
+
|
162
|
+
A Service registers with a server to make its services available. A client then searches the server for the service(s)
|
163
|
+
it is interested in and receives back all of the matching services known. The client can then connect to each service
|
164
|
+
and carry out whatever action is required. The search can be controlled, e.g. to return only local services or the first
|
165
|
+
service or services in a given environment.
|
166
|
+
|
167
|
+
Writing a Service is eased by various support classes. The main class is {JerbilService::Base}, which is a generic service
|
168
|
+
that deals with all of the Jerbil server interactions. By inheriting this class, a service can be created that uses
|
169
|
+
Jerbil with only a couple of lines of code.
|
170
|
+
|
171
|
+
Controlling a service is also made easy by the {JerbilService::Supervisor} class. This hides all of the actions needed
|
172
|
+
to start a service. However, service control is made even easier by /usr/bin/jserviced, which starts any service
|
173
|
+
given that services name, provided the files conform to certain protocols. The /usr/sbin/jservice-status and
|
174
|
+
/usr/sbin/jservice-stop commands work in a similar manner.
|
175
|
+
|
176
|
+
Writing a client is similarly made easy by the {JerbilService::Client} class, which finds one or more services ready
|
177
|
+
to be connected to and acted upon.
|
178
|
+
|
179
|
+
### Further Reading
|
180
|
+
|
181
|
+
* {file:README_SERVICES.md Jerbil Services} A short guide.
|
182
|
+
|
183
|
+
## Code Walkthrough
|
184
|
+
|
185
|
+
The Jerbil code is available online at [GitHub](https://github.com/osburn-sharp/jerbil)
|
186
|
+
Jerbil is divided into two groups: the server and the services that use the server.
|
187
|
+
|
188
|
+
### Servers
|
189
|
+
|
190
|
+
The server consists of one main class: {Jerbil::Broker} and two data-type classes:
|
191
|
+
{Jerbil::Servers} and {Jerbil::ServiceRecord}. The Broker contains the main server code,
|
192
|
+
finding and registering with other servers, accepting and recording services and
|
193
|
+
responding to queries about registered services. When a service registers with
|
194
|
+
the broker, the broker will also inform all of the other servers of that service.
|
195
|
+
The {Jerbil::Servers} class is used by the broker to record information
|
196
|
+
about a server and it provides convenience methods to connect to a server and a class
|
197
|
+
method to find the local server. The {Jerbil::ServiceRecord} class fulfils a similar
|
198
|
+
role for services.
|
199
|
+
|
200
|
+
When a server starts up it uses the {Jerbil::Servers.find_servers} class method to
|
201
|
+
search the network for any other servers. This method uses the NetAddr gem to create a
|
202
|
+
list of network addresses from the parameters defined by {Jerbil::Config}, allowing
|
203
|
+
users to limit jerbil addresses, both to control which machines get polled and to speed
|
204
|
+
up the search. The jerbil port is polled using the standard library TCPSocket interface
|
205
|
+
with a timeout (standard library again). When the server finds another server it first
|
206
|
+
does a minor security check (see below) and then obtains all of the services registered with
|
207
|
+
that server and adds it to its service database.
|
208
|
+
|
209
|
+
Jerbil is intended to be as reliable as possible - to survive any of the servers and
|
210
|
+
their services leaving the network unexpectedly. If a local client attempts to connect
|
211
|
+
to a remote service and fails, then the server will be asked to check with {Jerbil::Broker#service_missing?}.
|
212
|
+
This will attempt to contact the service's local server and ask it to check. If
|
213
|
+
the local server is running, it will check that the service is OK and if not, remove
|
214
|
+
the service and update all the other servers. If this server is not available (e.g.
|
215
|
+
server has gone down) then the original server will take responsibility for purging the
|
216
|
+
service from its own records and all the remaining servers.
|
217
|
+
|
218
|
+
Jerbil uses Jellog for logging and on :debug level produces copious records to help understand
|
219
|
+
what it is doing. See below for more detail on Jerbil logging.
|
220
|
+
|
221
|
+
Jerbil and Jerbil Services use the standard library daemons to run in the background
|
222
|
+
but they keep track of their own pids instead of relying on daemon. To stop the server,
|
223
|
+
an attempt is made to call the stop method, which cleans up with all the other servers,
|
224
|
+
but failing that the pid is used to kill the server.
|
225
|
+
|
226
|
+
### Services
|
227
|
+
|
228
|
+
Services are created by inheriting the {JerbilService::Base} class:
|
229
|
+
|
230
|
+
module RubyTest
|
231
|
+
|
232
|
+
extend JerbilService::Support
|
233
|
+
|
234
|
+
class Service < JerbilService::Base
|
235
|
+
|
236
|
+
def initialize(pkey, options)
|
237
|
+
super(:rubytest, pkey, options)
|
238
|
+
end
|
239
|
+
|
240
|
+
def action
|
241
|
+
@logger.debug("Someone called the action method!")
|
242
|
+
return "Hello"
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
The Base class takes care of Jerbil registration and creates a Jellog logger instance variable. It also provides a stop
|
250
|
+
method and a verify method. Full details on creating Jerbil Services are provided in the {file:README_SERVICES.md Readme file}.
|
251
|
+
|
252
|
+
### Security
|
253
|
+
|
254
|
+
Security is currently limited:
|
255
|
+
|
256
|
+
* Servers share a secret key, recorded in their config file which should be readable by limited people! They use this
|
257
|
+
key to ensure that registering servers are bona fide.
|
258
|
+
|
259
|
+
* Each server is given its own session-unique private key that it shares with the others and checks for all of the remote server methods.
|
260
|
+
The same key is also required to stop the server.
|
261
|
+
|
262
|
+
* A similar key is provided to each service and is required to stop the service. It is up to service writers to decide
|
263
|
+
whether to require clients to use this key, but a check_key method is provided.
|
264
|
+
|
265
|
+
The purpose of these checks is largely to protect integrity rather than make Jerbil secure. Jerbil is currently
|
266
|
+
targetted at a benign network environment.
|
267
|
+
|
268
|
+
During installation, jerbil-install should have created the 'jermine' user. By default the jerbil servers will run as this
|
269
|
+
user and so will any services started using the /etc/init.d runscripts.
|
270
|
+
|
271
|
+
### Logging
|
272
|
+
|
273
|
+
Jerbil uses [Jellog](http://rubydoc.info/github/osburn-sharp/jellog/frames) to log
|
274
|
+
messages. By default, these messages will be logged to /var/log/jerbil/jerbil-<env>.log
|
275
|
+
where <env> is prod, test or dev depending on the environment jerbil is running in.
|
276
|
+
Note that Jellog also logs certain messages to syslog unless you select the option
|
277
|
+
to disable this (see sbin/jerbild or /etc/conf.d/jerbild). The verbosity of the log
|
278
|
+
can be increased by setting the log_level parameter in {Jerbil::Config} to either
|
279
|
+
:verbose or :debug.
|
280
|
+
|
281
|
+
This log is used by the main Jerbil Server, but it is not the only log generated.
|
282
|
+
During start-up, the script will also log to a separate file (by default this is
|
283
|
+
/var/log/jerbil/jerbil_sd.log). This can be used to check if something went wrong
|
284
|
+
with the script after it daemonised. To ensure that this logging occurs, set the
|
285
|
+
command line option for the sbin/jerbild script or the parameter in /etc/conf.d/jerbild.
|
286
|
+
Alternatively, run the script in foreground mode to see all the possible messages. This
|
287
|
+
is also an option on the script and in the config file.
|
288
|
+
|
289
|
+
In the event that something goes wrong during the daemonisation itself, the jerbil
|
290
|
+
script sets the daemon process to log any messages to a file in the same log directory:
|
291
|
+
/var/log/jerbil/jerbil_daemon.output. This can, however, contain a lot of exception
|
292
|
+
information that has very little to do with the problem.
|
293
|
+
|
294
|
+
Finally, if Jerbil fails to start and these logs show no help, it is worth checking
|
295
|
+
the file /tmp/jerbil_panic.log which is also created by the startup script in the
|
296
|
+
event that an exception is raised during the daemonization process.
|
297
|
+
|
298
|
+
|
299
|
+
## Dependencies
|
300
|
+
|
301
|
+
A ruby compiler - works with 1.8.7.
|
302
|
+
|
303
|
+
Check the {file:Gemfile} for other dependencies.
|
304
|
+
|
305
|
+
## Documentation
|
306
|
+
|
307
|
+
Documentation is best viewed using Yard and is available online
|
308
|
+
at [RubyDoc](http://rubydoc.info/github/osburn-sharp/jerbil/frames)
|
309
|
+
|
310
|
+
## Testing/Modifying
|
311
|
+
|
312
|
+
Details of testing can be found in the {file:README_TESTING.md Testing README}.
|
313
|
+
|
314
|
+
## Bugs
|
315
|
+
|
316
|
+
Details of any unresolved bugs and change requests are in {file:Bugs.rdoc Bugs}
|
317
|
+
|
318
|
+
## Changelog
|
319
|
+
|
320
|
+
See {file:History.txt} for a summary change history
|
321
|
+
|
322
|
+
## Copyright and Licence
|
323
|
+
|
324
|
+
Copyright (c) 2012 Robert Sharp
|
325
|
+
|
326
|
+
This software is licensed under the terms defined in {file:LICENCE.rdoc}
|
327
|
+
|
328
|
+
The author may be contacted by via [GitHub](http://github.com/osburn-sharp)
|
329
|
+
|
330
|
+
## Warranty
|
331
|
+
|
332
|
+
This software is provided "as is" and without any express or implied
|
333
|
+
warranties, including, without limitation, the implied warranties of
|
334
|
+
merchantibility and fitness for a particular purpose.
|
335
|
+
|
data/README_SERVICES.md
ADDED
@@ -0,0 +1,410 @@
|
|
1
|
+
# Jerbil Services
|
2
|
+
|
3
|
+
A short guide to writing services that use {file:README.md Jerbil}.
|
4
|
+
|
5
|
+
## What is Jerbil and why use it?
|
6
|
+
|
7
|
+
Jerbil provides a way of finding services available on a network (or standlone PC for that matter)
|
8
|
+
and connecting with them to use them.
|
9
|
+
|
10
|
+
For example, I want to run a video recorder service for each TV-card I have on the network,
|
11
|
+
and I want to connect these to a central server for scheduling programmes etc. Each service
|
12
|
+
registers with Jerbil so that a client can easily find where each service is running,
|
13
|
+
select the one needed and connect to it to schedule a specific recording.
|
14
|
+
|
15
|
+
Jerbil provides the Broker to connect services, but it also provides a framework for
|
16
|
+
writing the services and hides almost all of the inter-processor comms from the
|
17
|
+
developer. Anyone who has used DRb on its own will know that there is quite a bit of overhead
|
18
|
+
involved, made more difficult if you are not sure where the service you want will be running.
|
19
|
+
You can use a service like Rinda, but its not exactly reliable: if the server goes down you have
|
20
|
+
to restart everything on the network.
|
21
|
+
|
22
|
+
## Some Examples of Jerbil Services
|
23
|
+
|
24
|
+
* An X10 controller - can be used by multiple applications to control X10 devices.
|
25
|
+
|
26
|
+
* A Thermostatic Temperature Logger - logs temperature data from simple sensors and also
|
27
|
+
provides a thermostat that can be programmed to trigger events by remote users
|
28
|
+
|
29
|
+
* A Central Heating Programmer - capable of controlling multiple devices/zones etc
|
30
|
+
using the above services
|
31
|
+
|
32
|
+
* A Media Player that can be controlled from the web
|
33
|
+
|
34
|
+
* A Music Player with multiple interfaces (CLI, LIRC, Web) running on multiple PCs to
|
35
|
+
share music around the house
|
36
|
+
|
37
|
+
* A network Disk manager - to show disc status, space etc for the whole network in one
|
38
|
+
easy location
|
39
|
+
|
40
|
+
* A Gentoo Portage Information Service - see what's installed across the network
|
41
|
+
|
42
|
+
## Getting Started
|
43
|
+
|
44
|
+
### Step 1 - Register your service
|
45
|
+
|
46
|
+
Before you start, you need to allocate a port number for your service that will be recognised across
|
47
|
+
the network. The standard way of doing this is to add it to /etc/services. There are more details
|
48
|
+
in the main {file:README.md readme}. Make sure this information is consistent across the network.
|
49
|
+
|
50
|
+
### Step 2 - Create a service class
|
51
|
+
|
52
|
+
The easiest way to develop a service using Jerbil is to inherit the {JerbilService::Base} class.
|
53
|
+
That way, all of the registration work is done for you without you even having to worry about it.
|
54
|
+
All you need to worry about is what methods you want your service to offer its clients:
|
55
|
+
|
56
|
+
#(file: lib/my_service.rb)
|
57
|
+
require 'jerbil_service/base'
|
58
|
+
require 'jerbil_service/support'
|
59
|
+
|
60
|
+
module MyService
|
61
|
+
|
62
|
+
# NOTE: make sure you call this class 'Service' if you
|
63
|
+
# want to use jerbil's support to run it etc
|
64
|
+
#
|
65
|
+
class Service < JerbilService::Base
|
66
|
+
|
67
|
+
# add some additional support class methods into the mix
|
68
|
+
extend JerbilService::Support
|
69
|
+
|
70
|
+
def initialize(pkey, options)
|
71
|
+
|
72
|
+
# do anything that needs to be done before the
|
73
|
+
# DRb service is started
|
74
|
+
|
75
|
+
# example parameter, see below
|
76
|
+
@greeting = options[:welcome]
|
77
|
+
|
78
|
+
super(:my_service, pkey, options)
|
79
|
+
|
80
|
+
# Ready to go
|
81
|
+
@logger.info "MyService ready to go"
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
attr_reader :greeting
|
86
|
+
|
87
|
+
# a method that your service provides
|
88
|
+
def whoami
|
89
|
+
return "This is my service"
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
In this very simple example, the service is set up with DRb and registered with the Jerbil
|
96
|
+
server when the parent {JerbilService::Base#initialize initialize} method is called. It also sets up a logger using
|
97
|
+
Jellog (see dependencies in {file:Gemfile}). After that, clients will be able to find
|
98
|
+
the service and call its method.
|
99
|
+
|
100
|
+
### Step 3 - Create a config class and file
|
101
|
+
|
102
|
+
A Jerbil Service expects to be receive a fair few parameters through the options hash passed
|
103
|
+
into the initialize method. It would be fairly painful to set this hash manually, but luckily
|
104
|
+
it is not necessary if you use [Jeckyl](https://github.com/osburn-sharp/jeckyl). What
|
105
|
+
you need to do is to add your own config class that inherits all of the parameters that
|
106
|
+
Jerbil is expecting to find:
|
107
|
+
|
108
|
+
#(file:lib/my_service/config.rb)
|
109
|
+
require 'jerbil_service/config'
|
110
|
+
|
111
|
+
module MyService
|
112
|
+
|
113
|
+
class Config < JerbilService::Config
|
114
|
+
|
115
|
+
# define your parameter methods here
|
116
|
+
|
117
|
+
def configure_welcome(greeting)
|
118
|
+
default "Hello and Good Day!"
|
119
|
+
comment "Define a welcome greeting for users"
|
120
|
+
a_string(greeting)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
To find out more about defining parameters, check the Jeckyl documentation on
|
127
|
+
[RubyDoc](http://rdoc.info/github/osburn-sharp/jeckyl/frames).
|
128
|
+
|
129
|
+
There is no hard rule that requires this file to be named as it is, but if you use
|
130
|
+
jerbil support (such as jserviced, see below) then this is what it will look for.
|
131
|
+
Btw, you can create a template for this file using the jeckyl command:
|
132
|
+
|
133
|
+
$ jeckyl klass MyService JerbilService::Config
|
134
|
+
# echoes template to stdout to ensure you are OK with it
|
135
|
+
$ jeckyl klass MyService JerbilService::Config > lib/my_service/config.rb
|
136
|
+
|
137
|
+
This defines your service-specific parameters and adds them to those that {JerbilService::Base}
|
138
|
+
is expecting. You now probably want to create a config file itself:
|
139
|
+
|
140
|
+
$ jeckyl config lib/my_service/config.rb -k
|
141
|
+
# everything looks OK?
|
142
|
+
$ jeckyl config lib/my_service/config.rb -k > test/conf.d/my_service.rb
|
143
|
+
|
144
|
+
The -k option ensures that all of the parameters for all parents are included in the
|
145
|
+
config file you have generated. You can now edit the generated config file to tweak
|
146
|
+
any of the defaults. For example, you probably want to log you development service
|
147
|
+
to a local directory instead of the system default and set the environment to :dev.
|
148
|
+
|
149
|
+
# Location for Jellog (logging utility) to save log files
|
150
|
+
log_dir "/home/user/dev/my_service/log"
|
151
|
+
|
152
|
+
# Set the default environment for service commands etc.
|
153
|
+
#
|
154
|
+
# Can be one of :prod, :test, :dev
|
155
|
+
environment :dev
|
156
|
+
|
157
|
+
A small warning - this is Ruby, which does not understand things like "~" in paths.
|
158
|
+
For testing, you may want to define a project_root at the top of the config file and
|
159
|
+
derive this from the __FILE__ constant. You can check your config file is not going
|
160
|
+
to cause you problems again with the jeckyl command:
|
161
|
+
|
162
|
+
$ jeckyl check test/conf.d/my_service.rb
|
163
|
+
|
164
|
+
You should now be ready to launch your service.
|
165
|
+
|
166
|
+
### Step 4 - Launch the service
|
167
|
+
|
168
|
+
Jerbil provides a very quick way get your new service up and running: the 'jservice' script:
|
169
|
+
|
170
|
+
$ /usr/sbin/jserviced -s my_service -c test/conf.d/my_service.rb
|
171
|
+
|
172
|
+
This script provides a few options to control the way the service is launched:
|
173
|
+
|
174
|
+
* -n, --no-daemon - do not daemonize the service but run it in the foreground
|
175
|
+
|
176
|
+
* -l, --log-daemon - log any output from the daemon to its own log-file
|
177
|
+
|
178
|
+
* -S, --no-syslog - suppress log messages to syslog, i.e. during development of your service
|
179
|
+
|
180
|
+
* -c, --config - use the given config file instead of the default
|
181
|
+
|
182
|
+
* -V, --verbose - output more information while setting up the service
|
183
|
+
|
184
|
+
* -q, --quite - output no information while setting up the service
|
185
|
+
|
186
|
+
Note that jserviced will expect you to follow ruby gem file-naming conventions so that
|
187
|
+
it can locate your service both during testing and in the wild. In this case that
|
188
|
+
would mean having a 'lib' directory with a file called 'my_service.rb' containing
|
189
|
+
the above code.
|
190
|
+
|
191
|
+
If you fancy launching your service long-hand, you can use the {JerbilService:Supervisor}
|
192
|
+
class to help.
|
193
|
+
|
194
|
+
### Step 5 - Check the service is working
|
195
|
+
|
196
|
+
Use the 'jerbil' command to check that everything is OK:
|
197
|
+
|
198
|
+
$ jerbil services -v
|
199
|
+
There are 13 services registered with Jerbil:
|
200
|
+
my_service[:prod]@server.network.org:49203
|
201
|
+
my_service[prod]@server.network.org:49203 responded
|
202
|
+
|
203
|
+
This shows what services are registered and calls each service's {JerbilService::Base#verify verify} method to make sure
|
204
|
+
it is running.
|
205
|
+
|
206
|
+
There is also a script that can be used to check that a service is running: 'sbin/jservice-status'. Options
|
207
|
+
are the same as for 'sbin/jserviced' except most of them are ignored.
|
208
|
+
|
209
|
+
### Step 6 - Write a Client and run it
|
210
|
+
|
211
|
+
Connecting to a service from a client is made easy by {JerbilService::Client#find_services}. This hides all
|
212
|
+
of the Jerbil interactions and just serves up interfaces as discovered:
|
213
|
+
|
214
|
+
JerbilService::Client.find_services(:local, MyService, client_opts) do |service|
|
215
|
+
|
216
|
+
puts service.greeting
|
217
|
+
puts service.whoami
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
This will search Jerbil for the first MyService registered and then call the 'whoami'
|
222
|
+
method. You can also find multiple services with the ':all' option, in which case the
|
223
|
+
block is called for each retrieved service, or you just find the :first service, which
|
224
|
+
is useful when you know there is only one on the network.
|
225
|
+
|
226
|
+
### Step 7 - Stopping the service
|
227
|
+
|
228
|
+
Finally, you will want to stop the service at some point, and better to do this gracefully.
|
229
|
+
The most direct way is to use 'sbin/jservice-stop', again with the same options as jserviced.
|
230
|
+
This script uses {JerbilService::Supervisor#stop_service} to do the real work, and you can
|
231
|
+
use this interface directly if it suits.
|
232
|
+
|
233
|
+
## Understanding Services
|
234
|
+
|
235
|
+
To develop a service using {JerbilService::Base} you probably need to know a little bit more about
|
236
|
+
the following:
|
237
|
+
|
238
|
+
* Jerbil Conventions
|
239
|
+
* the options hash, containing various important parameters
|
240
|
+
* System methods and how to customise them
|
241
|
+
* Keys and PIDS
|
242
|
+
|
243
|
+
### Jerbil Conventions
|
244
|
+
|
245
|
+
The Jerbil support infrastructure makes certain assumptions about how a Jerbil Service project
|
246
|
+
is named and structured. Given a service name (e.g. jexten for an X10 controller), there
|
247
|
+
should be a module with the same name containing a class called Service that inherits
|
248
|
+
{JerbilService::Base}, all within the root gem file (e.g. jexten.rb):
|
249
|
+
|
250
|
+
(file: jexten.rb)
|
251
|
+
module Jexten
|
252
|
+
|
253
|
+
class Service < JerblService::Base
|
254
|
+
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
In addition, Jerbil expects (by convention) to find a gem subdirectory named after the
|
260
|
+
service and containing at least the following files:
|
261
|
+
|
262
|
+
* errors.rb - defining within the service module a general exception for the service
|
263
|
+
and more detailed exceptions inheriting this general exception class.
|
264
|
+
* version.rb - defining within the service module three constants: Version containing
|
265
|
+
a gem standard version string, Version_Date containing a date string in a format
|
266
|
+
that ruby can easily parse into a Date object, and Ident, being a string that combines
|
267
|
+
the service name, version and date.
|
268
|
+
|
269
|
+
Both these files are not strictly required for Jerbil Services but are expected by some
|
270
|
+
jerbil support gems (not yet available).
|
271
|
+
|
272
|
+
### JerbilService Parameters
|
273
|
+
|
274
|
+
The {JerbilService::Base} class expects to receive an options hash containing parameters about
|
275
|
+
the service itself and about the logger that the service will set up. This options hash is
|
276
|
+
best created using the [Jeckyl gem](https://github.com/osburn-sharp/jeckyl). To make things easier,
|
277
|
+
Jerbil provides its own {JerbilService::Config} class that defines the expected parameters and inherits
|
278
|
+
the logger parameters from the [Jellog gem](https://github.com/osburn-sharp/jellog). A full description
|
279
|
+
of each parameter is available by going to the {JerbilService::Config config class} and following the
|
280
|
+
'See Also' link.
|
281
|
+
|
282
|
+
The main parameter to consider is :environment, which can be one of :dev, :test and :prod. This allows you to run a
|
283
|
+
production quality service across the network, test a ready-to-release upgrade also across
|
284
|
+
the network, and develop the next release all in parallel.
|
285
|
+
|
286
|
+
### Customising System Methods
|
287
|
+
|
288
|
+
The {JerbilService::Base} class provides a number of "system" methods that you may want to interact with.
|
289
|
+
In general these should not be called directly, which is why they are suffixed with the rather
|
290
|
+
cumbersome '_callback'. For example, to verify a service, you should use {JerbilService::Client#verify}
|
291
|
+
which calls the appropriate 'verify_callback' method.
|
292
|
+
|
293
|
+
* verify_callback - this is the method called when, e.g. the 'jerbil' command is asked to verify that
|
294
|
+
the service is running. There should be no need to modify this, but if you do you need to pass in
|
295
|
+
the service key (see below) because it will be expecting it.
|
296
|
+
|
297
|
+
* stop_callback - called to stop the service and deals with de-registering and stopping DRb. You may need to
|
298
|
+
add other actions before or after these, but be sure to include 'super' in your modified version
|
299
|
+
|
300
|
+
* wait - this should not be altered and is used when the service is started - ensuring all the DRb calls
|
301
|
+
are dealt with in the same scope.
|
302
|
+
|
303
|
+
### Keys and PIDs
|
304
|
+
|
305
|
+
If you start a service using {JerbilService::Supervisor} (or through sbin/jserviced which uses this interface)
|
306
|
+
then this will automatically create a private key and save it to the key directory specified in the
|
307
|
+
config file (see above). This key is required by all of the above system methods and is deleted when you
|
308
|
+
close the service using the same interface (or sbin/jservice-stop).
|
309
|
+
|
310
|
+
However, when you register the service with Jerbil you get a service key which you can then use as a minor
|
311
|
+
security check on clients calling your methods. This is stored as part of the {Jerbil::ServiceRecord}
|
312
|
+
object, accessed through the 'key' attribute. Users can access the key when connecting to through
|
313
|
+
{JerbilService::Client} using the {JerbilService::Client#service_key} method. There is protected
|
314
|
+
method {JerbilService::Base#check_key} which will compare two keys and raise an exception if they
|
315
|
+
are not the same (logging the event as well).
|
316
|
+
|
317
|
+
{JerbilService::Supervisor} will also store the PID of the service when it starts it up, and uses this
|
318
|
+
PID if it cannot connect to the service when asking it to stop. Its a blunt instrument but a lot quicker
|
319
|
+
than having to do it manually.
|
320
|
+
|
321
|
+
## The Client-Server Interface
|
322
|
+
|
323
|
+
A service can provide a set of methods for clients to use, but how does the client use them? Simple.
|
324
|
+
By using the {JerbilService::Client.find_services} method a block will be invoked with a single variable.
|
325
|
+
This variable is a "proxy" for the service itself, as well as providing a few extra methods of its own.
|
326
|
+
In other words, when you invoke a method on this variable, it checks to see if it is a valid method
|
327
|
+
for the real service, and if it is it calls that method with the parameters passed to it.
|
328
|
+
DRb takes care of the rest of it.
|
329
|
+
|
330
|
+
What you need to bear in mind, however, is that the interface between the client and the server is
|
331
|
+
always mediated via DRb. This means that variables passed across this interface cannot always be
|
332
|
+
treated as if they were local. For example, if you obtain an object from a service and then modify it
|
333
|
+
the original object that the service still controls will be unchanged. This is true most
|
334
|
+
of the time because Jerbil does not mixin the DRb::DRbUndumped module. However, more
|
335
|
+
complex objects may still be passed by reference. For a full discussion of this behaviour
|
336
|
+
read the [DRb Overview](http://ruby-doc.org/stdlib-1.8.7/libdoc/drb/rdoc/DRb.html) in
|
337
|
+
the standard library.
|
338
|
+
|
339
|
+
## Starting Jerbil Services at boot time
|
340
|
+
|
341
|
+
Its convenient to start a service automatically, and Jerbil tries to make this easy.
|
342
|
+
However, what is on offer will probably only work out of the box for Gentoo.
|
343
|
+
|
344
|
+
There is only one runscript needed and if you followed the installation instructions
|
345
|
+
in {file:README.md the readme file} then this will already be installed as /etc/init.d/jserviced.
|
346
|
+
To set up a service involves the following:
|
347
|
+
|
348
|
+
* create a link to /etc/init.d/jserviced for your service, e.g. /etc/init.d/myserviced
|
349
|
+
* create a copy of /etc/conf.d/jserviced for your service, e.g. /etc/conf.d/myserviced.
|
350
|
+
Ensure these two files have the same name.
|
351
|
+
* edit the conf.d file as required.
|
352
|
+
* start the service: /etc/init.d/myserviced start
|
353
|
+
* add the service to your default runscripts: rc-update add myserviced default
|
354
|
+
|
355
|
+
The variables defined in the conf.d file are:
|
356
|
+
|
357
|
+
* NO_DAEMON - set to true to run the service in the foreground, usually to debug it.
|
358
|
+
Defaults to false.
|
359
|
+
* NO_SYSLOG - suppress messages that would normally be sent to syslog, again usually
|
360
|
+
for debug purposes. Defaults to false.
|
361
|
+
* CONF_FILE - path to the config file for this service. Defaults to whatever the default
|
362
|
+
location is for Jeckyl (e.g. /etc/jermine/myservice.rb)
|
363
|
+
* LOG_DAEMON - log the output from the daemon, which covers the startup process before
|
364
|
+
the service starts logging itself. By default this will be logged to a file named
|
365
|
+
after the service with the suffix _sd added, e.g. /var/log/jermine/myservice_sd.log
|
366
|
+
Defaults to false
|
367
|
+
* VERBOSE - output more information during the startup process. This does not affect
|
368
|
+
the level of logging for the service itself
|
369
|
+
* SERVICE_NAME - by default the service name will be determined from the runscript.
|
370
|
+
But if, like me, you always seem to want a "d" on the end, then use this to ensure
|
371
|
+
the script picks up the right service, e.g. SERVICE_NAME="myservice"
|
372
|
+
* SERVICE_USER - by default, the service user will be a user created when Jerbil was
|
373
|
+
installed (jermine). Change it here if you want someone else. See below for details.
|
374
|
+
* USES - enter any services that this service uses. Make sure each service is on a
|
375
|
+
separate line. e.g. 'USES="logger' on one line and 'net"' on the next. See the example
|
376
|
+
in /etc/conf.d/jserviced.
|
377
|
+
* NEEDS - enter any services that this service needs. Make sure this is at least "jerbild".
|
378
|
+
As above, make it one entry per line.
|
379
|
+
* DESCRIPTION - a one line description that will be displayed during start/stop operations.
|
380
|
+
|
381
|
+
All services are super-user'd to a given user for safety within the runscript. By default this is jermine
|
382
|
+
but you can set it to another user as above.
|
383
|
+
|
384
|
+
When setting USES and NEEDS remember that services defined as needed will restart this
|
385
|
+
service when they are restarted. Services that are just used do not restart this service
|
386
|
+
but will be started before it.
|
387
|
+
|
388
|
+
## Errors and Exceptions
|
389
|
+
|
390
|
+
If you define your parameters using Jeckyl and {JerbilService::Config} and check them
|
391
|
+
with the 'jeckyl check' command you have taken the first step to reducing errors with
|
392
|
+
you new service. Jerbil tries to keep on going without upsetting its services, so the
|
393
|
+
only errors you are likely to suffer are:
|
394
|
+
|
395
|
+
* {Jerbil::ServiceAlreadyRegistered} if, for example you
|
396
|
+
forget to change the environment variable and already have a service running in the
|
397
|
+
same environment
|
398
|
+
* {Jerbil::MissingServer} if the Jerbil Server is not running. Hopefully
|
399
|
+
you will only suffer that one if you have forgotten to start the server!
|
400
|
+
* {Jerbil::InvalidService} if you forgot to register your service in /etc/services.
|
401
|
+
|
402
|
+
All of the other exceptions defined by Jerbil should be rarely encountered. There are
|
403
|
+
three main classes if you want to trap errors generically: JerbilError for all exceptions;
|
404
|
+
JerbilServiceError for exceptions relating to registering services etc, and JerbilServerError
|
405
|
+
for those relating to server opertions.
|
406
|
+
|
407
|
+
|
408
|
+
|
409
|
+
|
410
|
+
|