hoodoo 1.0.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 (216) hide show
  1. checksums.yaml +7 -0
  2. data/bin/hoodoo +5 -0
  3. data/lib/hoodoo.rb +27 -0
  4. data/lib/hoodoo/active.rb +32 -0
  5. data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
  6. data/lib/hoodoo/active/active_record/base.rb +81 -0
  7. data/lib/hoodoo/active/active_record/creator.rb +134 -0
  8. data/lib/hoodoo/active/active_record/dated.rb +343 -0
  9. data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
  10. data/lib/hoodoo/active/active_record/finder.rb +606 -0
  11. data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
  12. data/lib/hoodoo/active/active_record/secure.rb +431 -0
  13. data/lib/hoodoo/active/active_record/support.rb +106 -0
  14. data/lib/hoodoo/active/active_record/translated.rb +87 -0
  15. data/lib/hoodoo/active/active_record/uuid.rb +80 -0
  16. data/lib/hoodoo/active/active_record/writer.rb +321 -0
  17. data/lib/hoodoo/client.rb +23 -0
  18. data/lib/hoodoo/client/augmented_array.rb +29 -0
  19. data/lib/hoodoo/client/augmented_base.rb +168 -0
  20. data/lib/hoodoo/client/augmented_hash.rb +23 -0
  21. data/lib/hoodoo/client/client.rb +354 -0
  22. data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
  23. data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
  24. data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
  25. data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
  26. data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
  27. data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
  28. data/lib/hoodoo/client/headers.rb +269 -0
  29. data/lib/hoodoo/communicators.rb +23 -0
  30. data/lib/hoodoo/communicators/fast.rb +44 -0
  31. data/lib/hoodoo/communicators/pool.rb +601 -0
  32. data/lib/hoodoo/communicators/slow.rb +84 -0
  33. data/lib/hoodoo/data.rb +51 -0
  34. data/lib/hoodoo/data/resources/caller.rb +39 -0
  35. data/lib/hoodoo/data/resources/errors.rb +28 -0
  36. data/lib/hoodoo/data/resources/log.rb +31 -0
  37. data/lib/hoodoo/data/resources/session.rb +26 -0
  38. data/lib/hoodoo/data/types/error_primitive.rb +27 -0
  39. data/lib/hoodoo/data/types/permissions.rb +40 -0
  40. data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
  41. data/lib/hoodoo/data/types/permissions_full.rb +28 -0
  42. data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
  43. data/lib/hoodoo/discovery.rb +20 -0
  44. data/lib/hoodoo/errors.rb +19 -0
  45. data/lib/hoodoo/errors/error_descriptions.rb +229 -0
  46. data/lib/hoodoo/errors/errors.rb +322 -0
  47. data/lib/hoodoo/generator.rb +139 -0
  48. data/lib/hoodoo/logger.rb +23 -0
  49. data/lib/hoodoo/logger/fast_writer.rb +27 -0
  50. data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
  51. data/lib/hoodoo/logger/logger.rb +387 -0
  52. data/lib/hoodoo/logger/slow_writer.rb +49 -0
  53. data/lib/hoodoo/logger/writer_mixin.rb +52 -0
  54. data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
  55. data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
  56. data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
  57. data/lib/hoodoo/middleware.rb +33 -0
  58. data/lib/hoodoo/presenters.rb +45 -0
  59. data/lib/hoodoo/presenters/base.rb +281 -0
  60. data/lib/hoodoo/presenters/base_dsl.rb +519 -0
  61. data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
  62. data/lib/hoodoo/presenters/embedding.rb +232 -0
  63. data/lib/hoodoo/presenters/types/array.rb +118 -0
  64. data/lib/hoodoo/presenters/types/boolean.rb +26 -0
  65. data/lib/hoodoo/presenters/types/date.rb +26 -0
  66. data/lib/hoodoo/presenters/types/date_time.rb +26 -0
  67. data/lib/hoodoo/presenters/types/decimal.rb +47 -0
  68. data/lib/hoodoo/presenters/types/enum.rb +55 -0
  69. data/lib/hoodoo/presenters/types/field.rb +158 -0
  70. data/lib/hoodoo/presenters/types/float.rb +26 -0
  71. data/lib/hoodoo/presenters/types/hash.rb +361 -0
  72. data/lib/hoodoo/presenters/types/integer.rb +26 -0
  73. data/lib/hoodoo/presenters/types/object.rb +117 -0
  74. data/lib/hoodoo/presenters/types/string.rb +53 -0
  75. data/lib/hoodoo/presenters/types/tags.rb +24 -0
  76. data/lib/hoodoo/presenters/types/text.rb +26 -0
  77. data/lib/hoodoo/presenters/types/uuid.rb +54 -0
  78. data/lib/hoodoo/services.rb +34 -0
  79. data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
  80. data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
  81. data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
  82. data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
  83. data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
  84. data/lib/hoodoo/services/discovery/discovery.rb +186 -0
  85. data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
  86. data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
  87. data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
  88. data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
  89. data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
  90. data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
  91. data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
  92. data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
  93. data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
  94. data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
  95. data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
  96. data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
  97. data/lib/hoodoo/services/middleware/interaction.rb +127 -0
  98. data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
  99. data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
  100. data/lib/hoodoo/services/services/context.rb +153 -0
  101. data/lib/hoodoo/services/services/implementation.rb +132 -0
  102. data/lib/hoodoo/services/services/interface.rb +934 -0
  103. data/lib/hoodoo/services/services/permissions.rb +250 -0
  104. data/lib/hoodoo/services/services/request.rb +189 -0
  105. data/lib/hoodoo/services/services/response.rb +316 -0
  106. data/lib/hoodoo/services/services/service.rb +141 -0
  107. data/lib/hoodoo/services/services/session.rb +729 -0
  108. data/lib/hoodoo/utilities.rb +12 -0
  109. data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
  110. data/lib/hoodoo/utilities/utilities.rb +380 -0
  111. data/lib/hoodoo/utilities/uuid.rb +44 -0
  112. data/lib/hoodoo/version.rb +17 -0
  113. data/spec/active/active_record/base_spec.rb +57 -0
  114. data/spec/active/active_record/creator_spec.rb +88 -0
  115. data/spec/active/active_record/dated_spec.rb +248 -0
  116. data/spec/active/active_record/error_mapping_spec.rb +360 -0
  117. data/spec/active/active_record/finder_spec.rb +744 -0
  118. data/spec/active/active_record/search_helper_spec.rb +384 -0
  119. data/spec/active/active_record/secure_spec.rb +435 -0
  120. data/spec/active/active_record/support_spec.rb +225 -0
  121. data/spec/active/active_record/translated_spec.rb +19 -0
  122. data/spec/active/active_record/uuid_spec.rb +72 -0
  123. data/spec/active/active_record/writer_spec.rb +272 -0
  124. data/spec/alchemy/alchemy-amq.rb +33 -0
  125. data/spec/client/augmented_array_spec.rb +15 -0
  126. data/spec/client/augmented_base_spec.rb +50 -0
  127. data/spec/client/augmented_hash_spec.rb +15 -0
  128. data/spec/client/client_spec.rb +955 -0
  129. data/spec/client/endpoint/endpoint_spec.rb +70 -0
  130. data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
  131. data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
  132. data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
  133. data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
  134. data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
  135. data/spec/client/headers_spec.rb +172 -0
  136. data/spec/communicators/fast_spec.rb +9 -0
  137. data/spec/communicators/pool_spec.rb +339 -0
  138. data/spec/communicators/slow_spec.rb +15 -0
  139. data/spec/data/resources/caller_spec.rb +156 -0
  140. data/spec/data/resources/errors_spec.rb +22 -0
  141. data/spec/data/resources/log_spec.rb +20 -0
  142. data/spec/data/resources/session_spec.rb +15 -0
  143. data/spec/data/types/error_primitive_spec.rb +15 -0
  144. data/spec/data/types/permissions_defaults_spec.rb +25 -0
  145. data/spec/data/types/permissions_full_spec.rb +44 -0
  146. data/spec/data/types/permissions_resources_spec.rb +34 -0
  147. data/spec/data/types/permissions_spec.rb +37 -0
  148. data/spec/errors/error_descriptions_spec.rb +98 -0
  149. data/spec/errors/errors_spec.rb +346 -0
  150. data/spec/integration/service_actions_spec.rb +112 -0
  151. data/spec/logger/fast_writer_spec.rb +18 -0
  152. data/spec/logger/logger_spec.rb +259 -0
  153. data/spec/logger/slow_writer_spec.rb +144 -0
  154. data/spec/logger/writers/file_writer_spec.rb +37 -0
  155. data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
  156. data/spec/logger/writers/stream_writer_spec.rb +38 -0
  157. data/spec/presenters/base_dsl_spec.rb +111 -0
  158. data/spec/presenters/base_spec.rb +871 -0
  159. data/spec/presenters/common_resource_fields_spec.rb +30 -0
  160. data/spec/presenters/embedding_spec.rb +87 -0
  161. data/spec/presenters/types/array_spec.rb +249 -0
  162. data/spec/presenters/types/boolean_spec.rb +51 -0
  163. data/spec/presenters/types/date_spec.rb +57 -0
  164. data/spec/presenters/types/date_time_spec.rb +59 -0
  165. data/spec/presenters/types/decimal_spec.rb +58 -0
  166. data/spec/presenters/types/enum_spec.rb +71 -0
  167. data/spec/presenters/types/field_spec.rb +77 -0
  168. data/spec/presenters/types/float_spec.rb +50 -0
  169. data/spec/presenters/types/hash_spec.rb +1069 -0
  170. data/spec/presenters/types/integer_spec.rb +50 -0
  171. data/spec/presenters/types/object_spec.rb +177 -0
  172. data/spec/presenters/types/string_spec.rb +65 -0
  173. data/spec/presenters/types/tags_spec.rb +56 -0
  174. data/spec/presenters/types/text_spec.rb +50 -0
  175. data/spec/presenters/types/uuid_spec.rb +46 -0
  176. data/spec/presenters/walk_spec.rb +198 -0
  177. data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
  178. data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
  179. data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
  180. data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
  181. data/spec/services/discovery/discovery_spec.rb +73 -0
  182. data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
  183. data/spec/services/discovery/results/for_http_spec.rb +37 -0
  184. data/spec/services/discovery/results/for_local_spec.rb +21 -0
  185. data/spec/services/discovery/results/for_remote_spec.rb +15 -0
  186. data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
  187. data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
  188. data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
  189. data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
  190. data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
  191. data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
  192. data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
  193. data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
  194. data/spec/services/middleware/middleware_cors_spec.rb +93 -0
  195. data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
  196. data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
  197. data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
  198. data/spec/services/middleware/middleware_logging_spec.rb +356 -0
  199. data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
  200. data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
  201. data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
  202. data/spec/services/middleware/middleware_public_spec.rb +238 -0
  203. data/spec/services/middleware/middleware_spec.rb +1569 -0
  204. data/spec/services/middleware/string_inquirer_spec.rb +30 -0
  205. data/spec/services/services/application_spec.rb +74 -0
  206. data/spec/services/services/context_spec.rb +48 -0
  207. data/spec/services/services/implementation_spec.rb +45 -0
  208. data/spec/services/services/interface_spec.rb +262 -0
  209. data/spec/services/services/permissions_spec.rb +249 -0
  210. data/spec/services/services/request_spec.rb +95 -0
  211. data/spec/services/services/response_spec.rb +250 -0
  212. data/spec/services/services/session_spec.rb +432 -0
  213. data/spec/spec_helper.rb +298 -0
  214. data/spec/utilities/utilities_spec.rb +537 -0
  215. data/spec/utilities/uuid_spec.rb +20 -0
  216. metadata +615 -0
@@ -0,0 +1,195 @@
1
+ ########################################################################
2
+ # File:: by_drb.rb
3
+ # (C):: Loyalty New Zealand 2015
4
+ #
5
+ # Purpose:: Discover resource endpoint locations via a DRb registry. For
6
+ # HTTP-based endpoints.
7
+ # ----------------------------------------------------------------------
8
+ # 03-Mar-2015 (ADH): Created.
9
+ ########################################################################
10
+
11
+ require 'drb/drb'
12
+
13
+ module Hoodoo
14
+ module Services
15
+ class Discovery # Just used as a namespace here
16
+
17
+ # Discover resource endpoint locations via a DRb registry. For
18
+ # HTTP-based endpoints.
19
+ #
20
+ class ByDRb < Hoodoo::Services::Discovery
21
+
22
+ public
23
+
24
+ # Intended for testing only - flushes the records held in the
25
+ # DRb service.
26
+ #
27
+ def flush_services_for_test
28
+ drb_service().flush()
29
+ end
30
+
31
+ protected
32
+
33
+ # Configure an instance. Call via
34
+ # Hoodoo::Services::Discovery::Base#new. Parameters:
35
+ #
36
+ # +options+:: Options hash as described below.
37
+ #
38
+ # Options are:
39
+ #
40
+ # +drb_port+:: Optional port on which to launch the DRb service.
41
+ # If omitted, environment variable
42
+ # +HOODOO_DISCOVERY_BY_DRB_PORT_OVERRIDE+ will be
43
+ # consulted. If unset, port 8787 is used.
44
+ #
45
+ # +drb_uri+:: Optional URI String at which to find an existing DRB
46
+ # service. It must alreayd be running. If omitted, the
47
+ # +drb_port+ option's behaviour applies. If present,
48
+ # the +drb_port+ option is ignored.
49
+ #
50
+ def configure_with( options )
51
+ @drb_port = options[ :drb_port ]
52
+ @drb_uri = options[ :drb_uri ]
53
+ end
54
+
55
+ # Announce the location of an instance through the DRb service
56
+ # (which may be started up if necessary).
57
+ #
58
+ # Returns a Hoodoo::Services::Discovery::ForHTTP instance.
59
+ #
60
+ # Call via Hoodoo::Services::Discovery::Base#announce.
61
+ #
62
+ # +resource+:: Passed to #discover_remote.
63
+ # +version+:: Passed to #discover_remote.
64
+ # +options+:: Options hash as described below.
65
+ #
66
+ # Options keys are currently all required:
67
+ #
68
+ # +host+:: Host name as a string for location of service endpoint,
69
+ # over HTTP (usually, local development is assumed).
70
+ #
71
+ # +port+:: Port number of service endpoint.
72
+ #
73
+ # +path+:: Path on the above host and port of service endpoint.
74
+ #
75
+ def announce_remote( resource, version, options = {} )
76
+
77
+ host = options[ :host ]
78
+ port = options[ :port ]
79
+ path = options[ :path ]
80
+
81
+ endpoint_uri_string = "http://#{ host }:#{ port }#{ path }"
82
+
83
+ # Announce our local services if we managed to find the host and port,
84
+ # but no point otherwise; the values could be anything. In a 'guard'
85
+ # based environment, first-run determines host and port but subsequent
86
+ # runs do not - yet it stays the same, so it works out OK there.
87
+ #
88
+ unless host.nil? || port.nil? || discover_remote( resource, version )
89
+ drb_service().add( resource, version, endpoint_uri_string )
90
+ end
91
+
92
+ return result_for( resource, version, endpoint_uri_string )
93
+ end
94
+
95
+ # Discover an endpoint someone previously registered via
96
+ # #announce_remote.
97
+ #
98
+ # Returns a Hoodoo::Services::Discovery::ForHTTP instance if
99
+ # the endpoint is found, else +nil+.
100
+ #
101
+ # +resource+:: Resource name as a String.
102
+ # +version+:: Endpoint version as an Integer.
103
+ #
104
+ def discover_remote( resource, version )
105
+ endpoint_uri_string = drb_service().find( resource, version )
106
+ return result_for( resource, version, endpoint_uri_string )
107
+ end
108
+
109
+ private
110
+
111
+ # Construct a Hoodoo::Services::Discovery::ForHTTP instance for
112
+ # the given parameters.
113
+ #
114
+ # +resource+:: Resource name as a String.
115
+ # +version+:: Endpoint version as an Integer.
116
+ # +endpoint_uri_string+:: Endpoint location as a URI expressed
117
+ # as a String; may be +nil+.
118
+ #
119
+ # Returns the new instance, or +nil+ if the endpoint URI String
120
+ # was itself +nil+.
121
+ #
122
+ def result_for( resource, version, endpoint_uri_string )
123
+ if endpoint_uri_string.nil?
124
+ return nil
125
+ else
126
+ return Hoodoo::Services::Discovery::ForHTTP.new(
127
+ resource: resource,
128
+ version: version,
129
+ endpoint_uri: URI.parse( endpoint_uri_string )
130
+ )
131
+ end
132
+ end
133
+
134
+ # Start the DRb service on the port configured for this instance
135
+ # via its constructor and return a DRbObject instance to use for
136
+ # talking to it.
137
+ #
138
+ # Raises an exception if the DRb service cannot be started.
139
+ #
140
+ def drb_service
141
+
142
+ # Attempt to contact the DRb server daemon. If it can't be
143
+ # contacted, try to start it first, then connect.
144
+
145
+ if @drb_uri.nil? || @drb_uri.empty?
146
+ drb_uri = Hoodoo::Services::Discovery::ByDRb::DRbServer.uri( @drb_port )
147
+ start_on_localhost_if_not_already_running = true
148
+ else
149
+ drb_uri = @drb_uri
150
+ start_on_localhost_if_not_already_running = false
151
+ end
152
+
153
+ begin
154
+ drb_service = DRbObject.new_with_uri( drb_uri )
155
+ drb_service.ping()
156
+
157
+ rescue DRb::DRbConnError
158
+ if start_on_localhost_if_not_already_running
159
+ script_path = File.join( File.dirname( __FILE__ ), 'drb_server_start.rb' )
160
+ command = "bundle exec ruby '#{ script_path }'"
161
+ command << " --port #{ @drb_port }" unless @drb_port.nil? || @drb_port.empty?
162
+
163
+ Process.detach( spawn( command ) )
164
+
165
+ begin
166
+ Timeout::timeout( 5 ) do
167
+ loop do
168
+ begin
169
+ drb_service = DRbObject.new_with_uri( drb_uri )
170
+ drb_service.ping()
171
+ break
172
+ rescue DRb::DRbConnError
173
+ sleep 0.1
174
+ end
175
+ end
176
+ end
177
+
178
+ rescue Timeout::Error
179
+ raise "Hoodoo::Services::Discovery::ByDRb timed out while waiting for DRb service registry to start on local port #{ @drb_port }"
180
+
181
+ end
182
+
183
+ else
184
+ raise "Hoodoo::Services::Discovery::ByDRb could not contact a DRb service registry at #{ @drb_uri }"
185
+
186
+ end
187
+ end
188
+
189
+ return drb_service
190
+ end
191
+
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,166 @@
1
+ ########################################################################
2
+ # File:: drb_server.rb
3
+ # (C):: Loyalty New Zealand 2014
4
+ #
5
+ # Purpose:: For local development with no wider registration service
6
+ # like MemCache running, the first service that gets started
7
+ # will bring up a DRb server to record the location of other
8
+ # services as they start up. The subsequent services connect
9
+ # to the existing DRb server started by the first.
10
+ #
11
+ # This class is almost a private implementation detail of
12
+ # Hoodoo::Services::Middleware and is namespaced inside it.
13
+ # File "service_middleware.rb" must be "require"'d first.
14
+ # ----------------------------------------------------------------------
15
+ # 11-Nov-2014 (ADH): Split out from service_middleware.rb.
16
+ # 02-Mar-2015 (ADH): Moved into Discovery namespace.
17
+ ########################################################################
18
+
19
+ require 'hoodoo'
20
+
21
+ require 'drb/drb'
22
+ require 'drb/acl'
23
+
24
+ module Hoodoo; module Services; class Discovery # Just used as a namespace here
25
+ class ByDRb < Hoodoo::Services::Discovery # Also just used as a namespace here
26
+
27
+ # A registry of service endpoints, implenented as a DRB server class. An
28
+ # internal implementation detail of Hoodoo::Services::Middleware, in most
29
+ # respects.
30
+ #
31
+ class DRbServer
32
+
33
+ # URI for DRb server used during local machine development as a registry
34
+ # of service endpoints. Whichever service starts first runs the server
35
+ # which others connect to if subsequently started.
36
+ #
37
+ # +port+:: Optional integer port number for DRb service. If specified,
38
+ # this is used; else the +HOODOO_DISCOVERY_BY_DRB_PORT_OVERRIDE+
39
+ # environment variable is used; else a default of 8787 is
40
+ # chosen. Passing +nil+ explicitly also leads to the use of
41
+ # the environment variable or default value.
42
+ #
43
+ def self.uri( port = nil )
44
+
45
+ port ||= ENV[ 'HOODOO_DISCOVERY_BY_DRB_PORT_OVERRIDE' ] || 8787
46
+
47
+ # Use IP address, rather than 'localhost' here, to ensure that "address
48
+ # in use" errors are raised immediately if a second server startup
49
+ # attempt is made:
50
+ #
51
+ # https://bugs.ruby-lang.org/issues/3052
52
+ #
53
+ "druby://127.0.0.1:#{ port }"
54
+
55
+ end
56
+
57
+ # Start the DRb server. Does not return (joins the DRb thread). If the
58
+ # server is already running, expect an "address in use" connection
59
+ # exception from DRb.
60
+ #
61
+ # $SAFE will be set to 1 (unless it is already higher) in this thread.
62
+ #
63
+ # +port+:: Passed to ::uri method.
64
+ #
65
+ def self.start( port = nil )
66
+
67
+ uri = self.uri( port )
68
+
69
+ # For security, "disable eval() and friends":
70
+ #
71
+ # http://www.ruby-doc.org/stdlib-2.2.3/libdoc/drb/rdoc/DRb.html
72
+ # https://ruby-hacking-guide.github.io/security.html
73
+ # http://blog.recurity-labs.com/archives/2011/05/12/druby_for_penetration_testers/
74
+
75
+ $SAFE = 1
76
+
77
+ # Have to allow a tained port string from "outside" just to be able
78
+ # to start the service on a given port; so untaint that deliberately.
79
+ #
80
+ # http://ruby-doc.com/docs/ProgrammingRuby/html/taint.html
81
+
82
+ uri.untaint()
83
+ $stop_queue = ::Queue.new
84
+
85
+ ::DRb.start_service( uri,
86
+ FRONT_OBJECT,
87
+ :tcp_acl => LOCAL_ACL )
88
+
89
+ # DRB.thread.exit() does not reliably work; sometimes, it just hangs
90
+ # up. I don't know why. On OS X and under Travis, sporadic failures
91
+ # to return from the "stop()" method would result. Instead, we use a
92
+ # relatively elaborate queue; sit here waiting for a message to be
93
+ # pushed onto it, then just let this method exit naturally, ignoring
94
+ # the value that appeared on the queue.
95
+ #
96
+ # The sleep makes it more reliable too, indicating some kind of nasty
97
+ # race condition on start-vs-wait-to-shutdown.
98
+
99
+ sleep( 1 )
100
+ $stop_queue.pop()
101
+ end
102
+
103
+ # Create an instance ready for use as a DRb "front object".
104
+ #
105
+ def initialize
106
+ @repository = {}
107
+ end
108
+
109
+ # Check to see if this DRb service is awake. Returns +true+.
110
+ #
111
+ def ping
112
+ return true
113
+ end
114
+
115
+ # Add an endpoint to the list. If the endpoint was already added,
116
+ # it will be overwritten with the new data.
117
+ #
118
+ # +resource+:: Resource as a String or Symbol, e.g. "Product"
119
+ # +version+:: Endpoint's implemented API version as an Integer, e.g. 1
120
+ # +uri+:: URI at which this service may be accessed, including the
121
+ # endpoint path (e.g. "http://localhost:3002/v1/products"),
122
+ # as a String.
123
+ #
124
+ def add( resource, version, uri )
125
+ @repository[ "#{ resource }/#{ version }" ] = uri
126
+ end
127
+
128
+ # Find an endpoint in the list. Returns URI at which the service may be
129
+ # accessed as a String, or 'nil' if not found.
130
+ #
131
+ # +resource+:: Resource as a String or Symbol, e.g. "Product"
132
+ # +version+:: Endpoint's implemented API version as an Integer, e.g. 1
133
+ #
134
+ def find( resource, version )
135
+ @repository[ "#{ resource }/#{ version }" ]
136
+ end
137
+
138
+ # Flush out the repository, clearing all stored service records. This is
139
+ # usually for test purposes only.
140
+ #
141
+ def flush
142
+ @repository = {}
143
+ end
144
+
145
+ # Shut down this DRb service.
146
+ #
147
+ def stop
148
+ $stop_queue.push( true )
149
+ end
150
+ end
151
+
152
+ # Singleton "Front object" for the DRB service used in local development.
153
+ #
154
+ FRONT_OBJECT = Hoodoo::Services::Discovery::ByDRb::DRbServer.new
155
+
156
+ # Only allow connections from 127.0.0.1.
157
+ #
158
+ LOCAL_ACL = ACL.new( %w[
159
+ deny all
160
+ allow ::1
161
+ allow fe80::1%lo0
162
+ allow 127.0.0.1
163
+ ] )
164
+
165
+ end
166
+ end; end; end
@@ -0,0 +1,37 @@
1
+ ########################################################################
2
+ # File:: drb_server_start.rb
3
+ # (C):: Loyalty New Zealand 2014
4
+ #
5
+ # Purpose:: Run the DRB server. See service_registry_drb_server.rb.
6
+ # Usage:
7
+ #
8
+ # bundle exec ruby drb_server_start.rb
9
+ #
10
+ # There is usually no need to do this manually, as the
11
+ # middleware does it for you automatically.
12
+ # ----------------------------------------------------------------------
13
+ # 23-Dec-2014 (ADH): Created.
14
+ ########################################################################
15
+
16
+ require 'ostruct'
17
+ require 'optparse'
18
+
19
+ require 'hoodoo'
20
+
21
+ options = OpenStruct.new
22
+
23
+ OptionParser.new do | opts |
24
+ opts.banner = 'Usage: drb_server_start.rb [options]'
25
+
26
+ opts.on( '-p', '--port PORT', 'Listening port' ) do | val |
27
+ options.port = val || ENV[ 'HOODOO_DISCOVERY_BY_DRB_PORT_OVERRIDE' ] || 8787
28
+ end
29
+
30
+ opts.on( '-h', '--help', 'Prints this help' ) do
31
+ puts opts
32
+ exit
33
+ end
34
+ end.parse!
35
+
36
+ Process.setsid()
37
+ Hoodoo::Services::Discovery::ByDRb::DRbServer.start( options.port )
@@ -0,0 +1,186 @@
1
+ ########################################################################
2
+ # File:: discovery.rb
3
+ # (C):: Loyalty New Zealand 2015
4
+ #
5
+ # Purpose:: Support resource endpoint discovery.
6
+ # ----------------------------------------------------------------------
7
+ # 03-Mar-2015 (ADH): Created.
8
+ ########################################################################
9
+
10
+ module Hoodoo
11
+ module Services
12
+
13
+ # The service discovery mechanism is a way to find Resource
14
+ # implementations running inside service applications that may be
15
+ # available at HTTP URIs, over an AMQP queue or, potentially, any other
16
+ # system. Subclasses implement a particular distinct discovery approach.
17
+ # When implementations of services start up, they announce themselves
18
+ # (via Hoodoo::Services::Middleware) to the discovery engine. When other
19
+ # Resources (or Hoodoo::Client) want to find them, they query the same
20
+ # discovery engine to find out the original announcement information.
21
+ #
22
+ # Depending on how a discovery engine shares information about
23
+ # announced Resource endpoints, Resources might only be found if they are
24
+ # are on the same local machine; or the same remote host or queue; or
25
+ # they might perhaps be available even if scattered across multiple hosts
26
+ # and/or transport types.
27
+ #
28
+ # Implementations of service announcement and discovery code must be a
29
+ # subclass of this class, then optionally implement #configure_with and
30
+ # (almost certainly, but still optionally) #announce_remote; and must
31
+ # always implement #discover_remote.
32
+ #
33
+ class Discovery
34
+
35
+ public
36
+
37
+ # Create a new instance.
38
+ #
39
+ # +options+:: Passed to the subclass in use via #configure_with.
40
+ # Subclasses define their options. Only instantiate
41
+ # such subclasses, not this 'Base' class; see the
42
+ # subclass documentation for option details.
43
+ #
44
+ def initialize( options = {} )
45
+ @known_local_resources = {}
46
+ configure_with( options )
47
+ end
48
+
49
+ # Indicate that a resource is available locally and broacast its
50
+ # location to whatever discovery service a subclass supports via
51
+ # #announce_remote.
52
+ #
53
+ # +resource+:: Resource name as a Symbol or String (e.g.
54
+ # +:Purchase+).
55
+ #
56
+ # +version+:: Endpoint version as an Integer; optional; default
57
+ # is 1.
58
+ #
59
+ # +options+:: Defined by whatever subclass is in use. See that
60
+ # subclass's documentation for details.
61
+ #
62
+ # Returns the result of calling #announce_remote (in the subclass
63
+ # in use) with the same parameters. See the protected method
64
+ # definition in this base class for details.
65
+ #
66
+ def announce( resource, version = 1, options = {} )
67
+ resource = resource.to_sym
68
+ version = version.to_i
69
+ result = announce_remote( resource, version, options )
70
+
71
+ @known_local_resources[ resource ] ||= {}
72
+ @known_local_resources[ resource ][ version ] = result
73
+
74
+ return result
75
+ end
76
+
77
+ # Find a resource endpoint. This may be recorded locally or
78
+ # via whatever remote discovery mechanism a subclass implements.
79
+ #
80
+ # +resource+:: Resource name as a Symbol or String (e.g.
81
+ # +:Purchase+).
82
+ #
83
+ # +version+:: Endpoint version as an Integer; optional; default
84
+ # is 1.
85
+ #
86
+ # Returns the result of calling #discover_remote (in the subclass
87
+ # in use) with the same parameters. See the protected method
88
+ # definition in this base class for details.
89
+ #
90
+ # Use #is_local? if you need to know that an endpoint was
91
+ # announced through this same instance ("locally").
92
+ #
93
+ def discover( resource, version = 1 )
94
+ resource = resource.to_sym
95
+ version = version.to_i
96
+
97
+ if ( is_local?( resource, version ) )
98
+ return @known_local_resources[ resource ][ version ]
99
+ else
100
+ return discover_remote( resource, version )
101
+ end
102
+ end
103
+
104
+ # Was a resource announced in this instance ("locally")? Returns
105
+ # +true+ if so, else +false+.
106
+ #
107
+ # This only returns +true+ if #annouce has been called for the
108
+ # given resource and version.
109
+ #
110
+ # +resource+:: Resource name as a Symbol or String (e.g.
111
+ # +:Purchase+).
112
+ #
113
+ # +version+:: Endpoint version as an Integer; optional; default
114
+ # is 1.
115
+ #
116
+ def is_local?( resource, version = 1 )
117
+ resource = resource.to_sym
118
+ version = version.to_i
119
+
120
+ return @known_local_resources.has_key?( resource ) &&
121
+ @known_local_resources[ resource ].has_key?( version )
122
+ end
123
+
124
+ protected
125
+
126
+ # Configure a new instance. Subclasses optionally implement this
127
+ # method to store configuration information relevant to that
128
+ # subclass. Subclasses must document their options.
129
+ #
130
+ # +options+:: See subclass documentation for option details.
131
+ #
132
+ def configure_with( options )
133
+ # Implementation is optional and up to subclasses to do.
134
+ end
135
+
136
+ # Announce a resource endpoint. Subclasses optionally implement
137
+ # this method to broadcast information to other instances of the
138
+ # same subclass by some subclass-implemented mechanism.
139
+ #
140
+ # Discovery instance users do not call this method directly.
141
+ # Call #announce instead.
142
+ #
143
+ # Subclasses must return one of the Discovery "For" class instances,
144
+ # e.g. a Hoodoo::Services::Discovery::ForHTTP or
145
+ # Hoodoo::Services::Discovery::ForAMQP instance. This encapsulates
146
+ # the HTTP details required to contact the endpoint, or AMQP (queue)
147
+ # details required to contact the endpoint, respectively.
148
+ #
149
+ # Subclasses must return +nil+ if it has a problem announcing and
150
+ # cannot provide information for the given resource / version.
151
+ #
152
+ # +resource+:: Resource name as a String.
153
+ # +version+:: Endpoint version as an Integer.
154
+ # +options+:: See subclass documentation for option details.
155
+ #
156
+ def announce_remote( resource, version, options = {} )
157
+ # Implementation is optional and up to subclasses to do.
158
+ nil
159
+ end
160
+
161
+ # Discover the location of a resource endpoint. Subclasses _must_
162
+ # implement this method to retrieve information about the location
163
+ # of resource endpoints by some subclass-implemented mechanism.
164
+ #
165
+ # Discovery instance users do not call this method directly.
166
+ # Call #discover instead.
167
+ #
168
+ # Subclasses must return one of the Discovery "For" class instances,
169
+ # e.g. a Hoodoo::Services::Discovery::ForHTTP or
170
+ # Hoodoo::Services::Discovery::ForAMQP instance. This encapsulates
171
+ # the HTTP details required to contact the endpoint, or AMQP (queue)
172
+ # details required to contact the endpoint, respectively.
173
+ #
174
+ # If the requested endpoint is not found, subclasses must return
175
+ # +nil+.
176
+ #
177
+ # +resource+:: Resource name as a String.
178
+ # +version+:: Endpoint version as an Integer.
179
+ #
180
+ def discover_remote( resource, version )
181
+ raise "Hoodoo::Services::Discovery::Base subclass does not implement remote discovery required for resource '#{ resource }' / version '#{ version }'"
182
+ end
183
+
184
+ end
185
+ end
186
+ end