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,58 @@
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
+
16
+ module JerbilService
17
+
18
+ # Support methods that should be used to extend a service module
19
+ #
20
+ # module RubyTest
21
+ # extend JerbilService::Support
22
+ #
23
+ # class Service ...
24
+ #
25
+ #
26
+ module Support
27
+
28
+ # get the config settings for the given service module.
29
+ #
30
+ # This uses Jeckyl configuration and defaults to the conf file for the service module
31
+ # in the location defined by Jeckyl::ConfigRoot.
32
+ #
33
+ # e.g. for RubyTest, it will default to /etc/jermine/rubytest.rb
34
+ #
35
+ # Provide a different filename to change this.
36
+ #
37
+ def get_config(config_file=nil)
38
+ # check that the config_file has been specified
39
+ if config_file.nil? then
40
+ # no, so set the default
41
+ config_file = Jeckyl::ConfigRoot + "/#{self.to_s.downcase}.rb"
42
+ end
43
+
44
+ # read the config file
45
+ return self::Config.new(config_file)
46
+
47
+ end
48
+
49
+ # get the identity of this module, assuming this has been
50
+ # set up and is maintained by Jevoom
51
+ # Returns 'n/a' otherwise
52
+ def ident
53
+ return self::Ident
54
+ rescue
55
+ return 'n/a'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ #
2
+ #
3
+ # = Jerbil SService Utilities
4
+ #
5
+ # == Methods to assist setting up a Jerbil 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
+ module JerbilService
20
+
21
+ # misc. methods for Jerbil scripts
22
+ module Utils
23
+
24
+ # convert a filename etc to a proper class name
25
+ # For example, converts 'my_service' to 'MyService'
26
+ #
27
+ # @param [String] string to convert to a classname
28
+ # @return [String] converted classname
29
+ def Utils.classify(string)
30
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
31
+ string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,230 @@
1
+ #
2
+ # = Jerbil Server Class
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
+ # A useful class for managing information about Jerbil Servers
15
+ #
16
+ #
17
+ #
18
+ require 'socket'
19
+ require 'timeout'
20
+ require 'netaddr'
21
+ require 'resolv'
22
+ require 'drb'
23
+
24
+ module Jerbil
25
+
26
+ # An informational class for servers on the network, used by brokers to keep track of each other
27
+ # and by services and clients to find the local broker
28
+ class Servers
29
+
30
+ # return a record for the local server
31
+ #
32
+ # @param [Symbol] env being one of :dev, :test, :prod, and defaulting to :prod
33
+ # @param [Boolean] check set true to ensure server is running
34
+ # @raise [Jerbil::MissingServer] if server is not running
35
+ #
36
+ def self.get_local_server(env=nil, check=false)
37
+ env ||= :prod
38
+ hostname = Socket.gethostname
39
+ ip = Resolv.getaddress(hostname)
40
+ port = self.get_port(env)
41
+ return self.new(hostname, '', env, port) if ! check || self.server_up?(ip, port, 0.1)
42
+ raise Jerbil::MissingServer, "Cannot find local server on #{hostname}"
43
+ end
44
+
45
+ # create the local server record
46
+ #
47
+ # used by {Jerbil::Broker} during startup to create its own record
48
+ #
49
+ # @param [Symbol] env being one of :dev, :test, :prod
50
+ # @param [String] pkey the private key for this server
51
+ # @return [Jerbil::Servers] a server record
52
+ def self.create_local_server(env, pkey)
53
+ hostname = Socket.gethostname
54
+ port = self.get_port(env)
55
+ return self.new(hostname, pkey, env, port)
56
+ end
57
+
58
+ # return the server with the given fqdn
59
+ # @deprecated NOT USED ANYWHERE?
60
+ def self.get_server(servers, fqdn, env=:prod)
61
+ servers.each {|server| return server if server.fqdn == fqdn && server.env == env}
62
+ # found nothing to match
63
+ return nil
64
+ end
65
+
66
+ # scan the lan for open ports and return an array of servers
67
+ # for each.
68
+ #
69
+ # These server records have blank keys. {Jerbil::Broker#register_server} will return
70
+ # the called servers key, which can then be set with {Jerbil::Servers#set_key}.
71
+ #
72
+ # The netaddress and netmask are defined in {Jerbil::Config} where further details
73
+ # about these parameters can be found.
74
+ #
75
+ # @param [Symbol] env being the environment in which the servers should be running
76
+ # @param [String] netaddress for the network to search
77
+ # @param [Numeric] netmask to limit the search
78
+ # @param [Float] seconds to wait before timing out
79
+ # @return [Array] of {jerbil::Servers} that were found
80
+ def self.find_servers(env, netaddress, netmask, seconds=0.1)
81
+ # get the port number for jerbil
82
+ port = self.get_port(env)
83
+ naddr = NetAddr::CIDR.create("#{netaddress}/#{netmask.to_s}")
84
+ servers = []
85
+
86
+ naddr.enumerate.each do |ip|
87
+ servers << self.new(Resolv.getname(ip), '', env, port) if self.server_up?(ip, port, seconds)
88
+ end
89
+ # servers.each do |server|
90
+ # server.get_key
91
+ # end
92
+ return servers
93
+ end
94
+
95
+ # create a new server record with
96
+ #
97
+ # @param [String] fqdn - string fully qualified domain name
98
+ # @param [String] key - private access key for system methods
99
+ # @param [Symbol] env to set the environment to :dev, :test or :prod
100
+ # @param [Numeric] port - optional integer for port number
101
+ #
102
+ def initialize(fqdn, key, env=:prod, port=nil)
103
+ @fqdn = fqdn
104
+ @key = key
105
+ @env = env
106
+ if port.nil? then
107
+ @port = Socket.getservbyname('jerbil')
108
+ @port += 1 if env == :test
109
+ @port += 2 if env == :dev
110
+ else
111
+ @port = port
112
+ end
113
+ @active = false
114
+ end
115
+
116
+ # the full-qualified domain name for the server
117
+ attr_reader :fqdn
118
+
119
+ # the private key for the server
120
+ attr_reader :key
121
+
122
+ # the port for the server
123
+ attr_reader :port
124
+
125
+ # the environment that the server is running in
126
+ attr_reader :env
127
+
128
+ # test for equality
129
+ #
130
+ # ensure equality is across name, key and port.
131
+ # no need to test env because port is unique enough
132
+ #
133
+ # @param [Jerbil::Servers] rhs server to compare
134
+ def ==(rhs)
135
+ @fqdn == rhs.fqdn && @key == rhs.key && @port == rhs.port
136
+ end
137
+
138
+ # convenience method to assist Jerbil internals with DRb
139
+ #
140
+ # drb_address makes it easier to start a DRb server, which
141
+ # is done outside this class because it should only be done
142
+ # under specific circumstances, and not by the general users of this class
143
+ #
144
+ # @return [String] the servers DRb address
145
+ def drb_address
146
+ "druby://#{@fqdn}:#{@port}"
147
+ end
148
+
149
+ # connect to this server.
150
+ #
151
+ # Always assumes that the caller has a DRb service
152
+ # running. Jerbil certainly should!
153
+ #
154
+ # @raise [ServerConnectError] if anything goes wrong
155
+ def connect
156
+ DRbObject.new(nil, self.drb_address)
157
+ rescue Exception
158
+ raise ServerConnectError
159
+ end
160
+
161
+ # get the key for this server from the actual server
162
+ # @return [String] private key
163
+ def get_key
164
+ @key = self.connect.get_key
165
+ end
166
+
167
+ # save the key for this server, ensuring it is local
168
+ #
169
+ # @param [String] pkey - the server's private key'
170
+ def set_key(pkey)
171
+ @key = pkey
172
+ end
173
+
174
+ # return a string name for the server
175
+ #
176
+ # @return [String] ident string
177
+ def ident
178
+ "#{@fqdn}[:#{@env}]"
179
+ end
180
+
181
+ # create a deep copy of the server object.
182
+ #
183
+ # @return [Jerbil::Server] copy of self
184
+ def copy
185
+ return ServerRecord.new(self.fqdn, self.key, self.env, self.port)
186
+ end
187
+
188
+ # check if the given server is running
189
+ #
190
+ # This is intended for internal use only by {Jerbil::Servers.find_servers}.
191
+ #
192
+ # @param [String] ip address of server to test
193
+ # @param [Integer] port for server
194
+ # @param [Float] timeout_secs to wait for a response
195
+ # @return [Boolean] true if server is up
196
+ def self.server_up?(ip, port, timeout_secs)
197
+ #puts "Checking for #{ip}:#{port}"
198
+ Timeout::timeout(timeout_secs) do
199
+ begin
200
+ TCPSocket.new(ip, port).close
201
+ true
202
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH
203
+ false
204
+ end
205
+ end
206
+ rescue Timeout::Error
207
+ false
208
+ end
209
+
210
+ # get the port for the Jerbil Server
211
+ #
212
+ # Note that jerbil and all its services expect
213
+ # three consecutive ports to be available, one for each environment.
214
+ # The first port should be :prod, then :test and finally :dev
215
+ #
216
+ # @param [Symbol] env the environment in which the servers are running
217
+ # @raise [Jerbil::MissingJerbilService] if jerbil is not in /etc/services
218
+ #
219
+ def self.get_port(env)
220
+ port = Socket.getservbyname('jerbil')
221
+ port += 1 if env == :test
222
+ port += 2 if env == :dev
223
+ return port
224
+ rescue SocketError
225
+ raise Jerbil::MissingJerbilService, "There is no service 'jerbil' in /etc/services"
226
+ end
227
+
228
+
229
+ end
230
+ end
@@ -0,0 +1,216 @@
1
+ #
2
+ # Jerbil Service Object
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
+ # Service contains information about a service registered with Jerbil
15
+ #
16
+ require 'jerbil/errors'
17
+ require 'socket'
18
+ require 'digest/sha1'
19
+
20
+ module Jerbil
21
+
22
+ # Define a service record for a service to register with Jerbil
23
+ #
24
+ # Used internally by {Jerbil::Broker} and {JerbilService} to record information about
25
+ # running services. Is made available to users through the {JerbilService::Client}
26
+ # interface where it can be used, for example, to get the service's key.
27
+ #
28
+ class ServiceRecord
29
+
30
+ # create a new service record object
31
+ #
32
+ # Note that the callback parameters do not really need to be considered
33
+ # if you are using {JerbilService::Base}
34
+ #
35
+ # @param [Symbol] name identifying the service - needs to match /etc/services
36
+ # or create fails with the exception InvalidService
37
+ # @param [Symbol] env identify the service's environment. Allows multiple
38
+ # services to operate for development etc
39
+ # @param [Symbol] verify_callback being the name of the method to call to check
40
+ # that the service is working
41
+ # @param [Symbol] stop_callback as above but the method stops the service
42
+ # @return [ServiceRecord] of course
43
+ # @raise [InvalidService] if the service is not registered through /etc/services
44
+ def initialize(name, env, verify_callback=:verify_callback, stop_callback=nil)
45
+ @host = Socket.gethostname
46
+ @name = name
47
+ begin
48
+ @port = Socket.getservbyname(@name.to_s)
49
+ rescue
50
+ raise InvalidService, "No service registered as: #{name}"
51
+ end
52
+
53
+ # now increment it if not production
54
+ @port += 1 if env == :test
55
+ @port += 2 if env == :dev
56
+
57
+ @env = env
58
+ @key = Digest::SHA1.hexdigest(Time.now.to_s + rand(12341234).to_s)[1..10]
59
+ @pid = Process.pid
60
+ @verify_callback = verify_callback
61
+ @stop_callback = stop_callback
62
+ @lock = nil
63
+ @address = @host + ':' + @port.to_s
64
+ @registered_at = nil
65
+ @access_count = 0
66
+ @accessed_at = nil
67
+ @close = true
68
+ end
69
+
70
+ # name of the service
71
+ attr_reader :name
72
+ # environment the service is running in
73
+ attr_reader :env
74
+ # the key needed to access the service
75
+ attr_reader :key
76
+ # the host on which the service is running
77
+ attr_reader :host
78
+ # the full DRb address used to contact the service
79
+ # This is only required by Jerbil and should not be needed
80
+ # by the casual user.
81
+ attr_reader :address
82
+ # the port used by the service
83
+ attr_reader :port
84
+ # the date/time at which the service was registered with Jerbil
85
+ attr_reader :registered_at
86
+ # the number of times the service has been accessed on Jerbil
87
+ # since it was registered
88
+ attr_reader :access_count
89
+ # the date/time at which the service was last accessed
90
+ attr_reader :accessed_at
91
+
92
+ # method to allow Jerbil to set when the service was registered
93
+ def register
94
+ @registered_at = Time.now
95
+ @accessed_at = @registered_at
96
+ end
97
+
98
+ # method to allow Jerbil to record an access to this service
99
+ def log_access
100
+ @accessed_at = Time.now
101
+ @access_count += 1
102
+ end
103
+
104
+ # return a string containing the name, env, host etc
105
+ def ident
106
+ "#{@name}[#{@env}]@#{@address}"
107
+ end
108
+
109
+ # return a hash containing the find arguments for self
110
+ def args
111
+ {:name=>@name, :env=>@env, :host=>@host, :key=>@key}
112
+ end
113
+
114
+ # compare services according to a set of arguments
115
+ #
116
+ # This will return true if the service matches the given keys. An argument of nil
117
+ # matches all services. Uses the same arguments as {Jerbil::Broker#find} except
118
+ # that it will ignore the :ignore_access argument!
119
+ #
120
+ # @param (see Jerbil::Broker#find)
121
+ # @option (see Jerbil::Broker#find)
122
+ #
123
+ def matches?(args={})
124
+ options = {:name => nil, :env => nil, :host=>nil, :key => nil}.merge(args)
125
+ is_equal = true
126
+ is_equal = @name == options[:name] unless options[:name].nil?
127
+ is_equal = is_equal && @env == options[:env] unless options[:env].nil?
128
+ is_equal = is_equal && @host == options[:host] unless options[:host].nil?
129
+ is_equal = is_equal && @key == options[:key] unless options[:key].nil?
130
+ return is_equal
131
+ end
132
+
133
+ # compares services directly and returns true if they have the same
134
+ # name, env, and host.
135
+ #
136
+ # Note that this ignores the service records key, allowing you to find
137
+ # instances of the same service e.g. that have previously been registered.
138
+ #
139
+ # @param [ServiceRecord] rhs service to compare to this one
140
+ def same_service?(rhs)
141
+ self.matches?(:name=>rhs.name, :env=>rhs.env, :host=>rhs.host)
142
+ end
143
+
144
+ # compares services directly and returns true if they have the same
145
+ # name, env, host and key
146
+ #
147
+ # @param (see Jerbil::ServiceRecord#same_service?)
148
+ def ==(rhs)
149
+ self.matches?(:name=>rhs.name, :env=>rhs.env, :host=>rhs.host, :key=>rhs.key)
150
+ end
151
+
152
+ # connect to the service represented by this record
153
+ #
154
+ # You do not need to use this method if you use {JerbilService::Client} to
155
+ # manage the client-server interface direct.
156
+ #
157
+ # This return a DRb session for the given service
158
+ # set verify to true (default) to call the
159
+ # services keep_alive method
160
+ #
161
+ # @param [Boolean] verify if the service is running immedaitely after connecting
162
+ # @raise [ServiceCallbackMissing] if the verify method in this record does not
163
+ # match the methods of the service being connected to (you have mucked it up!)
164
+ # @raise [ServiceConnectError] if any other exception is raised during the connect
165
+ # process.
166
+ def connect(verify=true)
167
+ self.start_drb_if_needed
168
+ service = DRbObject.new(nil, "druby://#{@address}")
169
+ key = service.send(@verify_callback, @key) if verify
170
+ return service
171
+ rescue NoMethodError
172
+ raise ServiceCallbackMissing
173
+ rescue
174
+ raise ServiceConnectError
175
+
176
+ end
177
+
178
+ # convenience method to assist JerbilService actions
179
+ #
180
+ # drb_address makes it easier to start a DRb server, which
181
+ # is done outside this class because it should only be done
182
+ # under specific circumstances, and not by the general users of this class
183
+ #
184
+ def drb_address
185
+ "druby://#{@host}:#{@port}"
186
+ end
187
+
188
+ # is the service local to the caller?
189
+ def local?
190
+ return @host == Socket.gethostname
191
+ end
192
+
193
+
194
+ # close the connection to the service
195
+ def close
196
+ DRb.stop_service if @close
197
+ end
198
+
199
+
200
+ protected
201
+
202
+ # ensures that there is a DRb session running before trying to use
203
+ # DRb services
204
+ def start_drb_if_needed
205
+ DRb.current_server
206
+ @close = false
207
+ rescue
208
+ DRb.start_service
209
+ @close = true
210
+ end
211
+
212
+ end
213
+
214
+
215
+
216
+ end