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.
Files changed (86) hide show
  1. data/Bugs.rdoc +66 -0
  2. data/Gemfile +12 -0
  3. data/History.txt +359 -0
  4. data/Intro.txt +5 -0
  5. data/LICENCE.rdoc +159 -0
  6. data/README.md +335 -0
  7. data/README_SERVICES.md +410 -0
  8. data/README_TESTING.md +47 -0
  9. data/bin/jerbil +62 -0
  10. data/bin/jerbil-install +56 -0
  11. data/etc/conf.d/jerbild +15 -0
  12. data/etc/conf.d/jserviced +39 -0
  13. data/etc/init.d/jerbild +55 -0
  14. data/etc/init.d/jserviced +59 -0
  15. data/etc/jerbil/jerbil-client.rb +2 -0
  16. data/etc/jerbil/jerbil.rb +83 -0
  17. data/lib/jerbil.rb +636 -0
  18. data/lib/jerbil/config.md +49 -0
  19. data/lib/jerbil/config.rb +67 -0
  20. data/lib/jerbil/errors.rb +74 -0
  21. data/lib/jerbil/jerbil_service/base.rb +191 -0
  22. data/lib/jerbil/jerbil_service/client.rb +325 -0
  23. data/lib/jerbil/jerbil_service/config.md +119 -0
  24. data/lib/jerbil/jerbil_service/config.rb +72 -0
  25. data/lib/jerbil/jerbil_service/sclient.rb +343 -0
  26. data/lib/jerbil/jerbil_service/support.rb +58 -0
  27. data/lib/jerbil/jerbil_service/utils.rb +35 -0
  28. data/lib/jerbil/servers.rb +230 -0
  29. data/lib/jerbil/service.rb +216 -0
  30. data/lib/jerbil/support.rb +160 -0
  31. data/lib/jerbil/thor/server.rb +76 -0
  32. data/lib/jerbil/thor/service.rb +74 -0
  33. data/lib/jerbil/version.rb +13 -0
  34. data/sbin/jerbil-status +120 -0
  35. data/sbin/jerbil-stop +139 -0
  36. data/sbin/jerbild +186 -0
  37. data/sbin/jservice-status +107 -0
  38. data/sbin/jservice-stop +94 -0
  39. data/sbin/jserviced +111 -0
  40. data/spec/jerbil_2_jerbil_spec.rb +87 -0
  41. data/spec/jerbil_client1_spec.rb +80 -0
  42. data/spec/jerbil_client_spec.rb +114 -0
  43. data/spec/jerbil_client_stop_spec.rb +24 -0
  44. data/spec/jerbil_daemonised/jerbil_local_spec.rb +81 -0
  45. data/spec/jerbil_daemonised/jerbil_remote_spec.rb +116 -0
  46. data/spec/jerbil_load.rb +48 -0
  47. data/spec/jerbil_local_spec.rb +91 -0
  48. data/spec/jerbil_missing_spec.rb +98 -0
  49. data/spec/jerbil_remote_spec.rb +117 -0
  50. data/spec/jerbil_remote_spec_bup.rb +168 -0
  51. data/spec/jerbil_service_error_spec.rb +56 -0
  52. data/spec/jerbil_service_spec.rb +41 -0
  53. data/spec/jerbil_support_spec.rb +69 -0
  54. data/spec/jservice_utils_spec.rb +38 -0
  55. data/spec/server_spec.rb +69 -0
  56. data/spec/server_update_spec.rb +28 -0
  57. data/spec/service_spec.rb +72 -0
  58. data/spec/spec_helper.rb +12 -0
  59. data/spec/test_env_spec.rb +53 -0
  60. data/test/bad_test_service.rb +31 -0
  61. data/test/conf.d/jerbil +36 -0
  62. data/test/conf.d/jerbil.conf +39 -0
  63. data/test/conf.d/jerbil.rb +55 -0
  64. data/test/conf.d/jerbil_local.rb +33 -0
  65. data/test/conf.d/jerbil_no_local.conf +39 -0
  66. data/test/conf.d/jerbil_old.rb +47 -0
  67. data/test/conf.d/jerbil_test.rb +35 -0
  68. data/test/conf.d/malformed +1 -0
  69. data/test/conf.d/missing_services +39 -0
  70. data/test/conf.d/ruby_test.rb +8 -0
  71. data/test/init.d/jerbild +14 -0
  72. data/test/jerbil.rb +51 -0
  73. data/test/jerbil_config.rb +8 -0
  74. data/test/jstop.rb +36 -0
  75. data/test/key.asc +1 -0
  76. data/test/lib/ruby_test.rb +37 -0
  77. data/test/lib/ruby_test/config.rb +56 -0
  78. data/test/lib/ruby_test/version.rb +13 -0
  79. data/test/pids/jerbil-prod.asc +1 -0
  80. data/test/pids/jerbil-prod.pid +1 -0
  81. data/test/pids/jerbil.pid +1 -0
  82. data/test/private_key_file.asc +3 -0
  83. data/test/service-stop.rb +86 -0
  84. data/test/service_mock.rb +94 -0
  85. data/test/test_service_client.rb +25 -0
  86. metadata +265 -0
@@ -0,0 +1,49 @@
1
+ # Jerbil::Config Parameters
2
+
3
+ The following parameters are defined in {Jerbil::Config} and should be used
4
+ in a configuration file. A default config file can be generated using:
5
+
6
+ jeckyl config lib/jerbil/config.rb
7
+
8
+ ## Parameters
9
+
10
+ * **net_mask**
11
+
12
+ A valid netmask for the hosts to search using the above net address. This should be
13
+ between 24 (a class C network) and 30, beyound which its not much of a network. If you only have a few
14
+ hosts it will be easier to restrict them to a small subnet.
15
+
16
+ To find out more about netmasks, go to [UnixWiz](http://www.unixwiz.net/techtips/netmask-ref.html).
17
+
18
+ Default: 26
19
+
20
+ * **scan_timeout**
21
+
22
+ Provide a timeout when searching for jerbil servers on the net during startup.
23
+ Depending on the size of the net mask this timeout may make the search long.
24
+ The default should work in most cases
25
+
26
+ Default: 0.1
27
+
28
+ * **net_address**
29
+
30
+ A valid IPv4 address for the LAN on which the servers will operate.
31
+ Note that the broker uses this address to search for all servers.
32
+ Therefore a large range will take a long time to search. Set the net_mask to limit this.
33
+
34
+ Default: "192.168.0.1"
35
+
36
+ * **secret**
37
+
38
+ A secret key available to all Jerbil Servers and used to authenticate the initial registration.
39
+ If security is an issue, ensure that this config file is readable only be trusted users
40
+
41
+ No default set
42
+
43
+
44
+ ## See Also
45
+
46
+ There are also parameters in:
47
+
48
+ * {JerbilService::Config}
49
+ * Jelly::Options
@@ -0,0 +1,67 @@
1
+ #
2
+ #
3
+ # = Jerbil Config
4
+ #
5
+ # == Update to use Jeckyl::Service
6
+ #
7
+ # Author:: Robert Sharp
8
+ # Copyright:: Copyright (c) 2012 Robert Sharp
9
+ # License:: Open Software Licence v3.0
10
+ #
11
+ # This software is licensed for use under the Open Software Licence v. 3.0
12
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
13
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
14
+ # must themselves be licensed under the Open Software Licence v. 3.0
15
+ #
16
+ #
17
+ #
18
+
19
+ require 'jerbil/jerbil_service/config'
20
+
21
+
22
+ module Jerbil
23
+
24
+ # Jeckyl config parameters for the Jerbil Server
25
+ #
26
+ # The config file will include all of the parameters defined in {JerbilService::Config}
27
+ # and its parents, such as key directories, logging parameters etc.
28
+ #
29
+ # @see file:lib/jerbil/config.md Jerbil Parameter Descriptions
30
+ # @see file:lib/jerbil/jerbil_service/config.md Jerbil Service Parameter Descriptions
31
+ class Config < JerbilService::Config
32
+
33
+ def configure_net_address(naddr)
34
+ default '192.168.0.1'
35
+ comment "A valid IPv4 address for the LAN on which the servers will operate.",
36
+ "Note that the broker uses this address to search for all servers.",
37
+ "Therefore a large range will take a long time to search. Set the net_mask to limit this."
38
+ a_matching_string(naddr, /\A\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\z/)
39
+ end
40
+
41
+ def configure_net_mask(nmask)
42
+ default 26
43
+ comment "A valid netmask for the hosts to search using the above net address. This should be",
44
+ "between 24 (a class C network) and 30, beyound which its not much of a network. If you only have a few",
45
+ "hosts it will be easier to restrict them to a small subnet.",
46
+ "",
47
+ "To find out more about netmasks, go to [UnixWiz](http://www.unixwiz.net/techtips/netmask-ref.html)."
48
+ in_range(nmask, 24, 30)
49
+ end
50
+
51
+ def configure_scan_timeout(tim)
52
+ default 0.1
53
+ comment "Provide a timeout when searching for jerbil servers on the net during startup.",
54
+ "Depending on the size of the net mask this timeout may make the search long.",
55
+ "The default should work in most cases"
56
+
57
+ a_type_of(tim, Numeric)
58
+ end
59
+
60
+ def configure_secret(scrt)
61
+ comment "A secret key available to all Jerbil Servers and used to authenticate the initial registration.",
62
+ "If security is an issue, ensure that this config file is readable only be trusted users"
63
+ a_type_of(scrt, String)
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,74 @@
1
+ #
2
+ # Description
3
+ #
4
+ # Author:: Robert Sharp
5
+ # Copyright:: Copyright (c) 2010 Robert Sharp
6
+ # License:: Open Software Licence v3.0
7
+ #
8
+ # This software is licensed for use under the Open Software Licence v. 3.0
9
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
10
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
11
+ # must themselves be licensed under the Open Software Licence v. 3.0
12
+ #
13
+ #
14
+
15
+ module Jerbil
16
+
17
+ # general error type
18
+ class JerbilError < RuntimeError; end
19
+
20
+ # create a class for all service related errors
21
+ class JerbilServiceError < JerbilError; end
22
+
23
+ # error while trying to connect to a Jerbil-registered service
24
+ class ServiceConnectError < JerbilServiceError; end
25
+
26
+ # the service being registered is not in /etc/services
27
+ class InvalidService < JerbilServiceError; end
28
+
29
+ # the config file used by the service raised an error
30
+ class ServiceConfigError < JerbilServiceError; end
31
+
32
+ # the key used was not the key created for this service
33
+ class InvalidServiceKey < JerbilServiceError; end
34
+
35
+ # there is no callback defined where one should be
36
+ class ServiceCallbackMissing < JerbilServiceError; end
37
+
38
+ # there is already a service with these details registered with the Jerbil Server
39
+ class ServiceAlreadyRegistered < JerbilServiceError; end
40
+
41
+ # the service being registered is not local
42
+ class ServiceNotLocal < JerbilServiceError; end
43
+
44
+ # tried to do something to a service without access
45
+ class UnauthorizedMethod < JerbilServiceError; end
46
+
47
+ # there is no service
48
+ class ServiceNotFound < JerbilServiceError; end
49
+
50
+ # create a class for all server related errors
51
+ class JerbilServerError < JerbilError; end
52
+
53
+ # authentication of a server etc failed
54
+ class JerbilAuthenticationError < JerbilError; end
55
+
56
+ # error in the jerbil config file
57
+ class JerbilConfigError < JerbilServerError; end
58
+
59
+ # no jerbil service
60
+ class MissingJerbilService < JerbilServerError; end
61
+
62
+ # failed to find the server in the list of servers provided in config
63
+ class MissingServer < JerbilServerError; end
64
+
65
+ # error while trying to connect to a Jerbil Server
66
+ class ServerConnectError < JerbilServerError; end
67
+
68
+ # the server key provided for a remote operation does not match any known server
69
+ class InvalidServerKey < JerbilServerError; end
70
+
71
+ # the master key provided for a system operation does not match this master key
72
+ class InvalidPrivateKey < JerbilServerError; end
73
+
74
+ end
@@ -0,0 +1,191 @@
1
+ #
2
+ # Description
3
+ #
4
+ # Author:: Robert Sharp
5
+ # Copyright:: Copyright (c) 2010 Robert Sharp
6
+ # License:: Open Software Licence v3.0
7
+ #
8
+ # This software is licensed for use under the Open Software Licence v. 3.0
9
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
10
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
11
+ # must themselves be licensed under the Open Software Licence v. 3.0
12
+ #
13
+ #
14
+ require 'jerbil/service'
15
+ require 'jerbil/servers'
16
+ require 'jerbil/errors'
17
+ require 'jerbil/config'
18
+ require 'jerbil/support'
19
+ require 'jellog'
20
+ require 'jellog/config'
21
+ require 'socket'
22
+ require 'drb'
23
+
24
+ # == Jerbil Service
25
+ #
26
+ # Designed to help create ruby services easily, hiding all of the interactions with
27
+ # Jerbil itself.
28
+ #
29
+ # To create a service, create a service module (e.g. RubyTest) and within that a
30
+ # service class (e.g. RubyTest::Service) that whose parent is JerbilService::Base
31
+ # You can add a config file by copying the jerbil_service/config template and changing
32
+ # the module to your module (e.g. RubyTest::Config). Finally, use the jerbil_service/support
33
+ # module to extend your base module to include the get_config method.
34
+ #
35
+ # module RubyTest
36
+ #
37
+ # extend JerbilService::Support
38
+ #
39
+ # class Service < JerbilService::Base
40
+ #
41
+ # def initialize(pkey, options)
42
+ #
43
+ # super(:rubytest, pkey, options)
44
+ #
45
+ # end
46
+ # end
47
+ # end
48
+ #
49
+ # See also the Client, MultiClient and SuperClient classes to assist with a complete solution.
50
+ #
51
+ #
52
+ module JerbilService
53
+
54
+ # == JerbilService::Base
55
+ #
56
+ # Parent class to be used for all services. Manages all interactions with Jerbil
57
+ # and sets up a Jellog logger.
58
+ #
59
+ # In general, services using this class are intended to be started and stopped
60
+ # using {JerbilService::Supervisor} and clients are expected to interact with the
61
+ # service through {JerbilService::Client}.
62
+ #
63
+ # Further details can be found in {file:README_SERVICES.md Jerbil Service Readme}
64
+ #
65
+ class Base
66
+
67
+ # create a Jerbil Service
68
+ #
69
+ # @param [String] name - symbol for the service needs to correspond with /etc/services
70
+ # @param [String] pkey - private key used for privileged methods
71
+ # @param [Hash] options for the service. The easiest way to generate this hash is to use
72
+ # Jeckyl and inherit the {JerbilService::Config} class. See class description for details of
73
+ # the options required by a service
74
+ def initialize(name, pkey, options)
75
+ @name = name.to_s.capitalize
76
+ @env = options[:environment]
77
+ @private_key = pkey
78
+
79
+ # change the process name
80
+ $0 = "#{@name.downcase}-#{@env}"
81
+
82
+ # start up a logger
83
+ log_opts = Jellog::Config.intersection(options)
84
+ @logger = Jellog::Logger.new(@name, log_opts)
85
+ # @logger.log_level = options[:log_level]
86
+
87
+ @exit = options[:exit_on_stop]
88
+
89
+ begin
90
+ @service = Jerbil::ServiceRecord.new(name, @env, :verify_callback, :stop_callback)
91
+
92
+ # register the service
93
+ # Note that if the options hash includes a :jerbil_env, then this
94
+ # will be passed to select a Jerbil system running at other than :prod, the default
95
+ # This is not documented above because it is for testing Jerbil only
96
+ @jerbil_server = Jerbil::Servers.get_local_server(options[:jerbil_env])
97
+
98
+ # now connect to it
99
+ jerbil = @jerbil_server.connect
100
+
101
+ # and register self
102
+ jerbil.register(@service)
103
+
104
+ # and start it - preventing anything nasty from coming over DRb
105
+ $SAFE = 1 unless options[:unsafe]
106
+ DRb.start_service(@service.drb_address, self)
107
+
108
+ rescue Jerbil::MissingServer
109
+ @logger.fatal("Cannot find a local Jerbil server")
110
+ raise
111
+ rescue Jerbil::JerbilConfigError => err
112
+ @logger.fatal("Error in Jerbil Config File: #{err.message}")
113
+ raise
114
+ rescue Jerbil::JerbilServiceError =>jerr
115
+ @logger.fatal("Error with Jerbil Service: #{jerr.message}")
116
+ raise
117
+ rescue Jerbil::ServerConnectError
118
+ @logger.fatal("Error connecting to Jerbil Server")
119
+ raise
120
+ rescue DRb::DRbConnError =>derr
121
+ @logger.fatal("Error setting up DRb Server: #{derr.message}")
122
+ raise Jerbil::ServerConnectError
123
+ end
124
+
125
+ @logger.system "Started service: #{@service.ident}"
126
+ end
127
+
128
+ # give access to {Jerbil::ServiceRecord} Record for this service
129
+ # @deprecated - unless I can find why I allowed it!
130
+ attr_reader :service
131
+
132
+ # return the DRb address for the service
133
+ # @deprecated - as above
134
+ def drb_address
135
+ @service.drb_address
136
+ end
137
+
138
+ # this is used by callers just to check that the service is running
139
+ # if caller is unaware of the key, this will fail
140
+ #
141
+ # @param [String] skey - private key given to the service when created
142
+ #
143
+ # This method is not intended to be called directly. It is usually
144
+ # called when connecting to the service (see {Jerbil::ServiceRecord#connect}).
145
+ #
146
+ def verify_callback(skey="")
147
+ check_key(skey, @service.key)
148
+ @logger.info "Verify called"
149
+ return true
150
+ end
151
+
152
+ # used to stop the service. Requires the private key
153
+ #
154
+ # @param [String] pkey - private key given when service is created
155
+ #
156
+ # This method is not intended to be called directly. To stop a service, the
157
+ # user should use the {JerbilService::Supervisor} class.
158
+ #
159
+ def stop_callback(pkey="")
160
+ check_key(pkey)
161
+ # deregister
162
+ jerbil = @jerbil_server.connect
163
+ jerbil.remove(@service)
164
+ @logger.system "Stopped service: #{@service.ident}"
165
+ @logger.close
166
+ # and stop the DRb service, to exit gracefully
167
+ DRb.stop_service
168
+ end
169
+
170
+ # wait for calls. This hides the DRb call to be made once the
171
+ # service is up and running. It is not intended to be called by
172
+ # anyone outside of {JerbilService::Supervisor}
173
+ #
174
+ def wait(pkey='')
175
+ check_key(pkey)
176
+ DRb.thread.join
177
+ end
178
+
179
+ protected
180
+
181
+ # convenience method to check that the given key is the object's private key
182
+ # and raise {Jerbil::InvalidServiceKey} if not
183
+ def check_key(key, my_key = @private_key)
184
+ if key != my_key then
185
+ @logger.debug "Key mismatch: given #{key} but need #{my_key}"
186
+ raise Jerbil::InvalidServiceKey, @logger.error("Call made with Invalid Service Key: #{key}")
187
+ end
188
+ end
189
+
190
+ end
191
+ end
@@ -0,0 +1,325 @@
1
+ #
2
+ # == Jerbil Client Interface
3
+ #
4
+ # Author:: Robert Sharp
5
+ # Copyright:: Copyright (c) 2011 Robert Sharp
6
+ # License:: Open Software Licence v3.0
7
+ #
8
+ # This software is licensed for use under the Open Software Licence v. 3.0
9
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
10
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
11
+ # must themselves be licensed under the Open Software Licence v. 3.0
12
+ #
13
+ #
14
+
15
+ require 'jerbil/config'
16
+ require 'jeckyl/errors'
17
+ require 'jerbil/errors'
18
+ require 'jerbil/servers'
19
+ require 'jerbil/service'
20
+ require 'socket'
21
+
22
+ module JerbilService
23
+
24
+
25
+ #
26
+ # JerbilService::Client provides a wrapper around a {JerbilService::Base} type service that hides
27
+ # all interaction with Jerbil itself, enabling client interfaces with a service to be constructed
28
+ # quickly and painlessly
29
+ #
30
+ # To use, call the {JerbilService::Client.find_services} method, which yields a block for
31
+ # each matching service registered with the Jerbil Server. The service's methods can then
32
+ # be called on the yielded object, which are then transparently passed back to the
33
+ # service itself.
34
+ #
35
+ # It should always be remembered that these method calls will be mediated through Ruby's
36
+ # DRb mechanisms and therefore access to objects across this interface may not be
37
+ # the same as accessing them directly. See {file:README_SERVICES.md Services Readme} for
38
+ # more details
39
+ #
40
+ class Client
41
+
42
+ private_class_method :new
43
+
44
+ # return the server's config options for the given file, or the default if none given
45
+ #
46
+ # @param [Object] modl must be the constant name of the service's module.
47
+ # @param [String] config_file is an optional path to a Jeckyl config file
48
+ #
49
+ # This method uses [Jeckyl]() to obtain the config parameters. The module name
50
+ # is expected to follow the guidelines for a JerbilService: create a module
51
+ # with the service's name (echoed in the gem name etc) and then a class
52
+ # called Service. The parameter passed in is the module name as a constant
53
+ #
54
+ # config = JerbilService::Client.get_config(MyService)
55
+ #
56
+ def self.get_config(modl, config_file=nil)
57
+ modl.get_config(config_file)
58
+ end
59
+
60
+ # Connect to a one or more instances of a service using Jerbil
61
+ #
62
+ #
63
+ # The method will search the Jerbil Server for services of the given module, as
64
+ # defined by the what parameter:
65
+ # * :first, yields the block once with the first service regardless of how many services there are
66
+ # and with no guarantee about which services this might be
67
+ # * :local, yields the service running on the same processor as the client
68
+ # * :all, yields the block for each service.
69
+ # Within the block, the user can call any of the service's methods, together with the
70
+ # instance methods of the client itself.
71
+ #
72
+ # For example, to find a service running on a particular host:
73
+ #
74
+ # JerbilService::Client.find_services(:all, MyService, opts) do |service|
75
+ # if service.host == 'a_server.network.com' then
76
+ # # found the services I am looking for, now do something
77
+ # ...
78
+ # end
79
+ # end
80
+ #
81
+ # To find a service running in a particular environment, you need to set the
82
+ # above option:
83
+ # opts = {:environment => :dev}
84
+ # JerbilService::Client.find_services(:all, MyService, opts) do ...
85
+ #
86
+ # If you want to use a config file to access this information, you can use
87
+ # {JerbilService::Client.get_config} and extract the env information:
88
+ #
89
+ # config = JerbilService::Client.get_config(MyService)
90
+ # opts[:environment] = config[:environment]
91
+ # ...
92
+ #
93
+ # The connection to the service is closed when the block exits.
94
+ #
95
+ # @param [Symbol] what being one of :first, :all or :local
96
+ # @param [Object] modl - constant for the service module
97
+ # @param [Hash] options - hash of the following
98
+ # @option options [Boolean] :quiet suppress messages on stdout (default: false)
99
+ # @option options [Boolean] :local find only the local service (default: false)
100
+ # @option options [IO] :output a file object or similar (not filename) to be used
101
+ # for output - defaults to $stderr. Is overridden with /dev/null if quiet.
102
+ # @option options [Boolean] :welcome output additional messages to stdout suitable for standalone operation
103
+ # @option options [Symbol] :environment being that which the service is operating in (:dev, :test, :prod)
104
+ # @yield [service] a client instance for each matching service registered with Jerbil
105
+ def self.find_services(what, modl, options={}, &block)
106
+
107
+ case what
108
+ when :first
109
+ select = :first
110
+ when :all
111
+ select = :all
112
+ when :local
113
+ select = :first
114
+ options[:local] = true
115
+ else
116
+ raise ArgumentError, "Find Services invalid search key: #{what}"
117
+ end
118
+
119
+ unless options[:environment]
120
+ # set the default environment if not already set
121
+ options[:environment] = :prod
122
+ end
123
+
124
+ new(modl, options) do |client|
125
+ client.connect(select, &block)
126
+ end
127
+ end
128
+
129
+ # backwards compatible method to connect to first service using environment
130
+ # defined in config_file which is read in from options[:config_file] or the default
131
+ # location (see get_config)
132
+ #
133
+ # @deprecated Use {JerbilService::Client.find_services} instead
134
+ #
135
+ # @param (see find_services)
136
+ # @option (see find_services)
137
+ #
138
+ def self.connect(modl, options={}, &block) # :yields: client
139
+
140
+ config_file = options[:config_file]
141
+ config = modl.get_config(config_file)
142
+
143
+ options[:environment] ||= config[:environment]
144
+ options[:jerbil_env] ||= config[:jerbil_env]
145
+
146
+ self.find_services(:first, modl, options, &block)
147
+
148
+ end
149
+
150
+
151
+ # @private create a client instance and call the block. Should be used internally only
152
+ def initialize(modl, options, &block)
153
+
154
+ @klass = modl::Service
155
+ name = modl.to_s
156
+ name_symbol = name.downcase.to_sym
157
+
158
+ quiet = options[:quiet]
159
+ unless quiet
160
+ @output = options[:output] || $stderr
161
+ else
162
+ @output = File.open('/dev/null', 'w')
163
+ end
164
+
165
+ @welcome = options[:welcome]
166
+
167
+ @output.puts "Welcome to #{name} (#{modl.ident})" if @welcome
168
+
169
+ unless quiet
170
+ @output.puts "Options:"
171
+ options.each {|key, val| @output.puts(" #{key}:#{val}") }
172
+ end
173
+
174
+ env = options[:environment]
175
+
176
+ service_opts = {:name=>name_symbol, :env=>env}
177
+ service_opts[:host] = Socket.gethostname if options[:local]
178
+
179
+ begin
180
+ # find jerbil
181
+ @jerbil_server = Jerbil::Servers.get_local_server(options[:jerbil_env])
182
+
183
+ # now connect to it
184
+ jerbil = @jerbil_server.connect
185
+
186
+ # and find all of the services
187
+ @services = []
188
+ @services = jerbil.find(service_opts)
189
+
190
+ if @services.length == 0 then
191
+ @output.puts "No services for #{name}[:#{env}] were found"
192
+ raise Jerbil::ServiceNotFound, "No services for #{name}[:#{env}] were found"
193
+ end
194
+
195
+ block.call(self)
196
+
197
+ @output.puts "Stopping the service" if @welcome
198
+ #output.close unless @output == $stderr
199
+
200
+ return nil # give the caller nothing back
201
+
202
+ rescue Jerbil::MissingServer
203
+ @output.puts("Cannot find a local Jerbil server")
204
+ raise
205
+ rescue Jerbil::JerbilConfigError => err
206
+ @output.puts("Error in Jerbil Config File: #{err.message}")
207
+ raise
208
+ rescue Jerbil::JerbilServiceError =>jerr
209
+ @output.puts("Error with Jerbil Service: #{jerr.message}")
210
+ raise
211
+ rescue Jerbil::ServerConnectError
212
+ @output.puts("Error connecting to Jerbil Server")
213
+ raise
214
+ rescue DRb::DRbConnError =>derr
215
+ @output.puts("Error setting up DRb Server: #{derr.message}")
216
+ raise Jerbil::ServerConnectError
217
+ end
218
+
219
+ end
220
+
221
+ #private
222
+
223
+ # @private connect to the client
224
+ def connect(index=:all, &block)
225
+
226
+ if index == :first then
227
+ @session = @services[0].connect
228
+ @service = @services[0]
229
+ block.call(self)
230
+
231
+ @session = nil
232
+ else
233
+ @services.each do |service|
234
+ @service = service
235
+ @session = service.connect
236
+ block.call(self)
237
+ end
238
+ end
239
+
240
+ end
241
+
242
+ # verify that the service is working
243
+ #
244
+ # This is really a no-op in that if the client has entered the block successfully, then
245
+ # the service that is passed to the block will be working and therefore this call
246
+ # will always return true. Consider it a placebo statement for a client interface
247
+ # that only wants to check the service is running!
248
+ #
249
+ # @return [Boolean] indicating if the services if running
250
+ #
251
+ def verify
252
+ @session != nil
253
+ end
254
+
255
+
256
+ # return the name of the host on which the service is running
257
+ #
258
+ # @return [String] FQDN of the host on which the service is running
259
+ def host
260
+ return @service.host
261
+ end
262
+
263
+ # return the service key for the given service, which can be used
264
+ # by the caller where a method requires. See {file:README_SERVICES.md Services Readme}
265
+ # for details about keys.
266
+ #
267
+ # @return [String] key to use to connect to service methods requiring it
268
+ def service_key
269
+ return @service.key
270
+ end
271
+
272
+ # @private allow client to pass on methods to the remote service
273
+ #
274
+ def method_missing(symb, *parameters)
275
+
276
+ # stop anyone from calling the stop method
277
+ raise Jerbil::UnauthorizedMethod if symb == :stop_callback
278
+
279
+ # make sure this is a valid method of the receiving instance
280
+ # This is needed cos sending an unknown method over DRb with $SAFE = 1
281
+ # raises a Security Error. Could catch this, but it might happen for
282
+ # different reasons so best not to.
283
+
284
+ # need to fix symbols problem
285
+ if String.instance_methods.first.instance_of?(String) then
286
+ # < 1.9 Ruby
287
+ method_id = symb.to_s
288
+ @output.puts "Setting method id to a string: #{method_id}"
289
+ else
290
+ method_id = symb
291
+ @output.puts "Ensuring method id is a symbol: #{method_id}"
292
+ end
293
+
294
+ unless @klass.instance_methods.include?(method_id)
295
+ @output.puts "Failed to find method id:"
296
+ @output.puts "#{@klass.instance_methods.inspect}"
297
+ raise NoMethodError, "Failed to find method: #{method_id}"
298
+ end
299
+
300
+ retries = 0
301
+ begin
302
+
303
+ @session.send(symb, *parameters)
304
+
305
+ rescue DRb::DRbConnError
306
+ # service did not respond nicely
307
+ @output.puts "Failed to connected to the service - checking its OK"
308
+ jerbil = @jerbil_server.connect
309
+ if jerbil.service_missing?(@service) then
310
+ # something nasty has occurred
311
+ @output.puts "Jerbil confirms the service is missing"
312
+ raise Jerbil::ServiceConnectError, "Service is missing"
313
+ else
314
+ # seems jerbil thinks its ok, so try again
315
+ retries += 1
316
+ @output.puts "Jerbil thinks the service is OK, retrying"
317
+ retry unless retries > 2
318
+ @output.puts "Retried too many times, giving up"
319
+ raise Jerbil::ServiceConnectError, "Service is not responding"
320
+ end
321
+ end
322
+ end
323
+
324
+ end
325
+ end