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.
- checksums.yaml +7 -0
- data/bin/hoodoo +5 -0
- data/lib/hoodoo.rb +27 -0
- data/lib/hoodoo/active.rb +32 -0
- data/lib/hoodoo/active/active_model/uuid_validator.rb +45 -0
- data/lib/hoodoo/active/active_record/base.rb +81 -0
- data/lib/hoodoo/active/active_record/creator.rb +134 -0
- data/lib/hoodoo/active/active_record/dated.rb +343 -0
- data/lib/hoodoo/active/active_record/error_mapping.rb +351 -0
- data/lib/hoodoo/active/active_record/finder.rb +606 -0
- data/lib/hoodoo/active/active_record/search_helper.rb +189 -0
- data/lib/hoodoo/active/active_record/secure.rb +431 -0
- data/lib/hoodoo/active/active_record/support.rb +106 -0
- data/lib/hoodoo/active/active_record/translated.rb +87 -0
- data/lib/hoodoo/active/active_record/uuid.rb +80 -0
- data/lib/hoodoo/active/active_record/writer.rb +321 -0
- data/lib/hoodoo/client.rb +23 -0
- data/lib/hoodoo/client/augmented_array.rb +29 -0
- data/lib/hoodoo/client/augmented_base.rb +168 -0
- data/lib/hoodoo/client/augmented_hash.rb +23 -0
- data/lib/hoodoo/client/client.rb +354 -0
- data/lib/hoodoo/client/endpoint/endpoint.rb +427 -0
- data/lib/hoodoo/client/endpoint/endpoints/amqp.rb +180 -0
- data/lib/hoodoo/client/endpoint/endpoints/auto_session.rb +194 -0
- data/lib/hoodoo/client/endpoint/endpoints/http.rb +203 -0
- data/lib/hoodoo/client/endpoint/endpoints/http_based.rb +367 -0
- data/lib/hoodoo/client/endpoint/endpoints/not_found.rb +59 -0
- data/lib/hoodoo/client/headers.rb +269 -0
- data/lib/hoodoo/communicators.rb +23 -0
- data/lib/hoodoo/communicators/fast.rb +44 -0
- data/lib/hoodoo/communicators/pool.rb +601 -0
- data/lib/hoodoo/communicators/slow.rb +84 -0
- data/lib/hoodoo/data.rb +51 -0
- data/lib/hoodoo/data/resources/caller.rb +39 -0
- data/lib/hoodoo/data/resources/errors.rb +28 -0
- data/lib/hoodoo/data/resources/log.rb +31 -0
- data/lib/hoodoo/data/resources/session.rb +26 -0
- data/lib/hoodoo/data/types/error_primitive.rb +27 -0
- data/lib/hoodoo/data/types/permissions.rb +40 -0
- data/lib/hoodoo/data/types/permissions_defaults.rb +32 -0
- data/lib/hoodoo/data/types/permissions_full.rb +28 -0
- data/lib/hoodoo/data/types/permissions_resources.rb +31 -0
- data/lib/hoodoo/discovery.rb +20 -0
- data/lib/hoodoo/errors.rb +19 -0
- data/lib/hoodoo/errors/error_descriptions.rb +229 -0
- data/lib/hoodoo/errors/errors.rb +322 -0
- data/lib/hoodoo/generator.rb +139 -0
- data/lib/hoodoo/logger.rb +23 -0
- data/lib/hoodoo/logger/fast_writer.rb +27 -0
- data/lib/hoodoo/logger/flattener_mixin.rb +36 -0
- data/lib/hoodoo/logger/logger.rb +387 -0
- data/lib/hoodoo/logger/slow_writer.rb +49 -0
- data/lib/hoodoo/logger/writer_mixin.rb +52 -0
- data/lib/hoodoo/logger/writers/file_writer.rb +45 -0
- data/lib/hoodoo/logger/writers/log_entries_dot_com_writer.rb +64 -0
- data/lib/hoodoo/logger/writers/stream_writer.rb +43 -0
- data/lib/hoodoo/middleware.rb +33 -0
- data/lib/hoodoo/presenters.rb +45 -0
- data/lib/hoodoo/presenters/base.rb +281 -0
- data/lib/hoodoo/presenters/base_dsl.rb +519 -0
- data/lib/hoodoo/presenters/common_resource_fields.rb +31 -0
- data/lib/hoodoo/presenters/embedding.rb +232 -0
- data/lib/hoodoo/presenters/types/array.rb +118 -0
- data/lib/hoodoo/presenters/types/boolean.rb +26 -0
- data/lib/hoodoo/presenters/types/date.rb +26 -0
- data/lib/hoodoo/presenters/types/date_time.rb +26 -0
- data/lib/hoodoo/presenters/types/decimal.rb +47 -0
- data/lib/hoodoo/presenters/types/enum.rb +55 -0
- data/lib/hoodoo/presenters/types/field.rb +158 -0
- data/lib/hoodoo/presenters/types/float.rb +26 -0
- data/lib/hoodoo/presenters/types/hash.rb +361 -0
- data/lib/hoodoo/presenters/types/integer.rb +26 -0
- data/lib/hoodoo/presenters/types/object.rb +117 -0
- data/lib/hoodoo/presenters/types/string.rb +53 -0
- data/lib/hoodoo/presenters/types/tags.rb +24 -0
- data/lib/hoodoo/presenters/types/text.rb +26 -0
- data/lib/hoodoo/presenters/types/uuid.rb +54 -0
- data/lib/hoodoo/services.rb +34 -0
- data/lib/hoodoo/services/discovery/discoverers/by_consul.rb +66 -0
- data/lib/hoodoo/services/discovery/discoverers/by_convention.rb +173 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +195 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server.rb +166 -0
- data/lib/hoodoo/services/discovery/discoverers/by_drb/drb_server_start.rb +37 -0
- data/lib/hoodoo/services/discovery/discovery.rb +186 -0
- data/lib/hoodoo/services/discovery/results/for_amqp.rb +58 -0
- data/lib/hoodoo/services/discovery/results/for_http.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_local.rb +85 -0
- data/lib/hoodoo/services/discovery/results/for_remote.rb +57 -0
- data/lib/hoodoo/services/middleware/amqp_log_message.rb +186 -0
- data/lib/hoodoo/services/middleware/amqp_log_writer.rb +119 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_local.rb +130 -0
- data/lib/hoodoo/services/middleware/endpoints/inter_resource_remote.rb +202 -0
- data/lib/hoodoo/services/middleware/exception_reporting/base_reporter.rb +105 -0
- data/lib/hoodoo/services/middleware/exception_reporting/exception_reporting.rb +115 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +64 -0
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/raygun_reporter.rb +63 -0
- data/lib/hoodoo/services/middleware/interaction.rb +127 -0
- data/lib/hoodoo/services/middleware/middleware.rb +2705 -0
- data/lib/hoodoo/services/middleware/rack_monkey_patch.rb +73 -0
- data/lib/hoodoo/services/services/context.rb +153 -0
- data/lib/hoodoo/services/services/implementation.rb +132 -0
- data/lib/hoodoo/services/services/interface.rb +934 -0
- data/lib/hoodoo/services/services/permissions.rb +250 -0
- data/lib/hoodoo/services/services/request.rb +189 -0
- data/lib/hoodoo/services/services/response.rb +316 -0
- data/lib/hoodoo/services/services/service.rb +141 -0
- data/lib/hoodoo/services/services/session.rb +729 -0
- data/lib/hoodoo/utilities.rb +12 -0
- data/lib/hoodoo/utilities/string_inquirer.rb +54 -0
- data/lib/hoodoo/utilities/utilities.rb +380 -0
- data/lib/hoodoo/utilities/uuid.rb +44 -0
- data/lib/hoodoo/version.rb +17 -0
- data/spec/active/active_record/base_spec.rb +57 -0
- data/spec/active/active_record/creator_spec.rb +88 -0
- data/spec/active/active_record/dated_spec.rb +248 -0
- data/spec/active/active_record/error_mapping_spec.rb +360 -0
- data/spec/active/active_record/finder_spec.rb +744 -0
- data/spec/active/active_record/search_helper_spec.rb +384 -0
- data/spec/active/active_record/secure_spec.rb +435 -0
- data/spec/active/active_record/support_spec.rb +225 -0
- data/spec/active/active_record/translated_spec.rb +19 -0
- data/spec/active/active_record/uuid_spec.rb +72 -0
- data/spec/active/active_record/writer_spec.rb +272 -0
- data/spec/alchemy/alchemy-amq.rb +33 -0
- data/spec/client/augmented_array_spec.rb +15 -0
- data/spec/client/augmented_base_spec.rb +50 -0
- data/spec/client/augmented_hash_spec.rb +15 -0
- data/spec/client/client_spec.rb +955 -0
- data/spec/client/endpoint/endpoint_spec.rb +70 -0
- data/spec/client/endpoint/endpoints/amqp_spec.rb +16 -0
- data/spec/client/endpoint/endpoints/auto_session_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_based_spec.rb +9 -0
- data/spec/client/endpoint/endpoints/http_spec.rb +103 -0
- data/spec/client/endpoint/endpoints/not_found_spec.rb +35 -0
- data/spec/client/headers_spec.rb +172 -0
- data/spec/communicators/fast_spec.rb +9 -0
- data/spec/communicators/pool_spec.rb +339 -0
- data/spec/communicators/slow_spec.rb +15 -0
- data/spec/data/resources/caller_spec.rb +156 -0
- data/spec/data/resources/errors_spec.rb +22 -0
- data/spec/data/resources/log_spec.rb +20 -0
- data/spec/data/resources/session_spec.rb +15 -0
- data/spec/data/types/error_primitive_spec.rb +15 -0
- data/spec/data/types/permissions_defaults_spec.rb +25 -0
- data/spec/data/types/permissions_full_spec.rb +44 -0
- data/spec/data/types/permissions_resources_spec.rb +34 -0
- data/spec/data/types/permissions_spec.rb +37 -0
- data/spec/errors/error_descriptions_spec.rb +98 -0
- data/spec/errors/errors_spec.rb +346 -0
- data/spec/integration/service_actions_spec.rb +112 -0
- data/spec/logger/fast_writer_spec.rb +18 -0
- data/spec/logger/logger_spec.rb +259 -0
- data/spec/logger/slow_writer_spec.rb +144 -0
- data/spec/logger/writers/file_writer_spec.rb +37 -0
- data/spec/logger/writers/log_entries_dot_com_writer_spec.rb +29 -0
- data/spec/logger/writers/stream_writer_spec.rb +38 -0
- data/spec/presenters/base_dsl_spec.rb +111 -0
- data/spec/presenters/base_spec.rb +871 -0
- data/spec/presenters/common_resource_fields_spec.rb +30 -0
- data/spec/presenters/embedding_spec.rb +87 -0
- data/spec/presenters/types/array_spec.rb +249 -0
- data/spec/presenters/types/boolean_spec.rb +51 -0
- data/spec/presenters/types/date_spec.rb +57 -0
- data/spec/presenters/types/date_time_spec.rb +59 -0
- data/spec/presenters/types/decimal_spec.rb +58 -0
- data/spec/presenters/types/enum_spec.rb +71 -0
- data/spec/presenters/types/field_spec.rb +77 -0
- data/spec/presenters/types/float_spec.rb +50 -0
- data/spec/presenters/types/hash_spec.rb +1069 -0
- data/spec/presenters/types/integer_spec.rb +50 -0
- data/spec/presenters/types/object_spec.rb +177 -0
- data/spec/presenters/types/string_spec.rb +65 -0
- data/spec/presenters/types/tags_spec.rb +56 -0
- data/spec/presenters/types/text_spec.rb +50 -0
- data/spec/presenters/types/uuid_spec.rb +46 -0
- data/spec/presenters/walk_spec.rb +198 -0
- data/spec/services/discovery/discoverers/by_consul_spec.rb +29 -0
- data/spec/services/discovery/discoverers/by_convention_spec.rb +67 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +80 -0
- data/spec/services/discovery/discoverers/by_drb/drb_server_spec.rb +205 -0
- data/spec/services/discovery/discovery_spec.rb +73 -0
- data/spec/services/discovery/results/for_amqp_spec.rb +17 -0
- data/spec/services/discovery/results/for_http_spec.rb +37 -0
- data/spec/services/discovery/results/for_local_spec.rb +21 -0
- data/spec/services/discovery/results/for_remote_spec.rb +15 -0
- data/spec/services/middleware/amqp_log_message_spec.rb +60 -0
- data/spec/services/middleware/amqp_log_writer_spec.rb +95 -0
- data/spec/services/middleware/endpoints/inter_resource_local_spec.rb +9 -0
- data/spec/services/middleware/endpoints/inter_resource_remote_spec.rb +9 -0
- data/spec/services/middleware/exception_reporting/base_reporter_spec.rb +16 -0
- data/spec/services/middleware/exception_reporting/exception_reporting_spec.rb +92 -0
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +24 -0
- data/spec/services/middleware/exception_reporting/reporters/raygun_reporter_spec.rb +23 -0
- data/spec/services/middleware/middleware_cors_spec.rb +93 -0
- data/spec/services/middleware/middleware_create_update_spec.rb +489 -0
- data/spec/services/middleware/middleware_dated_at_spec.rb +186 -0
- data/spec/services/middleware/middleware_exotic_communication_spec.rb +560 -0
- data/spec/services/middleware/middleware_logging_spec.rb +356 -0
- data/spec/services/middleware/middleware_multi_local_spec.rb +1094 -0
- data/spec/services/middleware/middleware_multi_remote_spec.rb +1440 -0
- data/spec/services/middleware/middleware_permissions_spec.rb +1014 -0
- data/spec/services/middleware/middleware_public_spec.rb +238 -0
- data/spec/services/middleware/middleware_spec.rb +1569 -0
- data/spec/services/middleware/string_inquirer_spec.rb +30 -0
- data/spec/services/services/application_spec.rb +74 -0
- data/spec/services/services/context_spec.rb +48 -0
- data/spec/services/services/implementation_spec.rb +45 -0
- data/spec/services/services/interface_spec.rb +262 -0
- data/spec/services/services/permissions_spec.rb +249 -0
- data/spec/services/services/request_spec.rb +95 -0
- data/spec/services/services/response_spec.rb +250 -0
- data/spec/services/services/session_spec.rb +432 -0
- data/spec/spec_helper.rb +298 -0
- data/spec/utilities/utilities_spec.rb +537 -0
- data/spec/utilities/uuid_spec.rb +20 -0
- 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
|