hoodoo 1.19.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hoodoo/active/active_model/uuid_validator.rb +0 -1
- data/lib/hoodoo/active/active_record/dated.rb +2 -2
- data/lib/hoodoo/active/active_record/support.rb +2 -1
- data/lib/hoodoo/active/active_record/uuid.rb +2 -2
- data/lib/hoodoo/active/active_record/writer.rb +1 -1
- data/lib/hoodoo/generator.rb +52 -12
- data/lib/hoodoo/monkey/patch/datadog_traced_amqp.rb +11 -6
- data/lib/hoodoo/monkey/patch/newrelic_middleware_analytics.rb +17 -10
- data/lib/hoodoo/monkey/patch/newrelic_traced_amqp.rb +71 -33
- data/lib/hoodoo/services/discovery/discoverers/by_drb/by_drb.rb +13 -8
- data/lib/hoodoo/services/middleware/exception_reporting/reporters/airbrake_reporter.rb +8 -3
- data/lib/hoodoo/services/middleware/interaction.rb +1 -1
- data/lib/hoodoo/services/middleware/middleware.rb +52 -41
- data/lib/hoodoo/version.rb +2 -2
- data/spec/active/active_record/creator_spec.rb +1 -1
- data/spec/active/active_record/dated_spec.rb +7 -7
- data/spec/active/active_record/finder_spec.rb +953 -839
- data/spec/active/active_record/manually_dated_spec.rb +1 -1
- data/spec/active/active_record/search_helper_spec.rb +1 -1
- data/spec/active/active_record/secure_spec.rb +2 -2
- data/spec/active/active_record/support_spec.rb +3 -3
- data/spec/monkey/patch/datadog_traced_amqp_spec.rb +10 -2
- data/spec/monkey/patch/newrelic_traced_amqp_spec.rb +54 -21
- data/spec/new_relic/agent/logger.rb +24 -0
- data/spec/new_relic/agent/transaction.rb +32 -0
- data/spec/services/discovery/discoverers/by_drb/by_drb_spec.rb +48 -2
- data/spec/services/middleware/exception_reporting/reporters/airbrake_reporter_spec.rb +4 -4
- data/spec/services/middleware/middleware_multi_local_spec.rb +41 -13
- data/spec/services/middleware/middleware_multi_remote_spec.rb +93 -67
- data/spec/services/middleware/middleware_spec.rb +80 -7
- data/spec/services/services/interface_spec.rb +2 -2
- data/spec/transient_store/transient_store/mocks/redis_spec.rb +8 -6
- metadata +30 -26
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc0db0a009b321df89a2eb2e8a879c76bc875bb9
|
4
|
+
data.tar.gz: 29c85538f05080287181a67d3292b72f950f1872
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8519c31178c7439fc57888851eac50409e71c8225dfc0fd5421da540e370ab01ef92423f3a5753195efea9ca01f4954ee68d33d09fc083470a6ac5eade962f70
|
7
|
+
data.tar.gz: 45d7b58441e42e224624123c74f15f812880a074ea07648628f49741a3a3ba918456632b456a6b617b73d65935884ec16ad30e92176e19cc939af93fcc3437c0
|
@@ -243,7 +243,7 @@ module Hoodoo
|
|
243
243
|
# Rationalise and convert the date time to UTC.
|
244
244
|
|
245
245
|
date_time = Hoodoo::Utilities.rationalise_datetime( date_time ).utc
|
246
|
-
safe_date_time = self.
|
246
|
+
safe_date_time = self.connection.quoted_date( date_time )
|
247
247
|
|
248
248
|
# Create strings that specify the required attributes escaped and
|
249
249
|
# joined by commas for use in a SQL query, for both main and history
|
@@ -269,7 +269,7 @@ module Hoodoo
|
|
269
269
|
SELECT #{ safe_history_name_string },"effective_start","effective_end"
|
270
270
|
FROM #{ dating_table_name }
|
271
271
|
) AS u
|
272
|
-
WHERE "effective_start" <= #{ safe_date_time } AND ("effective_end" > #{ safe_date_time } OR "effective_end" IS NULL)
|
272
|
+
WHERE "effective_start" <= '#{ safe_date_time }' AND ("effective_end" > '#{ safe_date_time }' OR "effective_end" IS NULL)
|
273
273
|
) AS #{ self.table_name }
|
274
274
|
}
|
275
275
|
|
@@ -70,7 +70,8 @@ module Hoodoo
|
|
70
70
|
#
|
71
71
|
mapping = {
|
72
72
|
'created_after' => Hoodoo::ActiveRecord::Finder::SearchHelper.cs_gt( :created_at ),
|
73
|
-
'created_before' => Hoodoo::ActiveRecord::Finder::SearchHelper.cs_lt( :created_at )
|
73
|
+
'created_before' => Hoodoo::ActiveRecord::Finder::SearchHelper.cs_lt( :created_at ),
|
74
|
+
'created_by' => Hoodoo::ActiveRecord::Finder::SearchHelper.cs_match( :created_by )
|
74
75
|
}
|
75
76
|
|
76
77
|
if mapping.keys.length != ( mapping.keys | Hoodoo::Services::Middleware::FRAMEWORK_QUERY_DATA.keys ).length
|
@@ -38,7 +38,7 @@ module Hoodoo
|
|
38
38
|
# # ...
|
39
39
|
# end
|
40
40
|
#
|
41
|
-
# +model+:: The ActiveRecord::Base descendant
|
41
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
42
42
|
# this module.
|
43
43
|
#
|
44
44
|
def self.included( model )
|
@@ -62,7 +62,7 @@ module Hoodoo
|
|
62
62
|
#
|
63
63
|
# change_column :model_table_name, :id, :string, :limit => 32
|
64
64
|
#
|
65
|
-
# +model+:: The ActiveRecord::Base descendant
|
65
|
+
# +model+:: The ActiveRecord::Base descendant that is including
|
66
66
|
# this module.
|
67
67
|
#
|
68
68
|
def self.instantiate( model )
|
data/lib/hoodoo/generator.rb
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
|
11
11
|
require 'singleton'
|
12
12
|
require 'fileutils'
|
13
|
+
require 'pathname'
|
13
14
|
|
14
15
|
module Hoodoo
|
15
16
|
|
@@ -45,25 +46,33 @@ module Hoodoo
|
|
45
46
|
|
46
47
|
name = args.first
|
47
48
|
|
48
|
-
return show_usage
|
49
|
+
return show_usage if name == '-h' || name == '--help' || name.nil? || name.empty?
|
50
|
+
return show_version if name == '-v' || name == '--version'
|
49
51
|
|
50
52
|
return usage_and_warning( "SERVICE_NAME must match #{ NAME_REGEX.inspect }" ) if naughty_name?( name )
|
51
53
|
return usage_and_warning( "'#{ name }' already exists" ) if File.exist?( "./#{ name }" )
|
52
54
|
|
53
|
-
|
55
|
+
path = args[ 2 ] if args[ 1 ] == '--path'
|
56
|
+
git = args[ 2 ] if args[ 1 ] == '--from'
|
54
57
|
git ||= 'git@github.com:LoyaltyNZ/service_shell.git'
|
55
58
|
|
56
|
-
return create_service( name, git )
|
59
|
+
return create_service( name, git, path )
|
57
60
|
end
|
58
61
|
|
59
62
|
private
|
60
63
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
# Name of new service, mandatory GitHub repo path of the shell to start
|
65
|
+
# with, or override local filesystem path to copy from (pass "nil" to
|
66
|
+
# not do that).
|
67
|
+
#
|
68
|
+
def create_service( name, git, path )
|
69
|
+
ok = create_dir( name )
|
70
|
+
ok = clone_service_shell( name, git ) if ok && path.nil?
|
71
|
+
ok = copy_service_shell( name, path ) if ok && ! path.nil?
|
72
|
+
ok = remove_dot_git( name, git ) if ok
|
73
|
+
ok = replace_strings( name ) if ok
|
74
|
+
|
75
|
+
if ok
|
67
76
|
puts "Success! ./#{name} created."
|
68
77
|
Kernel::exit( KERNEL_EXIT_SUCCESS )
|
69
78
|
else
|
@@ -81,6 +90,14 @@ module Hoodoo
|
|
81
90
|
$?.to_i == 0
|
82
91
|
end
|
83
92
|
|
93
|
+
def copy_service_shell( name, path )
|
94
|
+
source_path = Pathname.new( path ).to_s << '/.'
|
95
|
+
dest_path = File.join( '.', name )
|
96
|
+
|
97
|
+
FileUtils.cp_r( source_path, dest_path, verbose: true )
|
98
|
+
$?.to_i == 0
|
99
|
+
end
|
100
|
+
|
84
101
|
def remove_dot_git( name, git )
|
85
102
|
git_folder = "./#{ name }/.git"
|
86
103
|
git_config = "#{ git_folder }/config"
|
@@ -123,9 +140,32 @@ module Hoodoo
|
|
123
140
|
end
|
124
141
|
|
125
142
|
def show_usage
|
126
|
-
puts
|
127
|
-
puts "
|
128
|
-
puts
|
143
|
+
puts
|
144
|
+
puts "Creates a service shell at the PWD, customised with the given service name."
|
145
|
+
puts
|
146
|
+
puts " hoodoo <service-name> [--from <git-repository> | --path <full-pathname>]"
|
147
|
+
puts
|
148
|
+
puts "For example:"
|
149
|
+
puts
|
150
|
+
puts " hoodoo service_cron"
|
151
|
+
puts " hoodoo service_person --from git@github.com:YOURNAME/service_shell_fork.git"
|
152
|
+
puts " hoodoo service_product --path /path/to/local/service/shell/container"
|
153
|
+
puts
|
154
|
+
puts "See also:"
|
155
|
+
puts
|
156
|
+
puts " hoodoo --help shows this help"
|
157
|
+
puts " hoodoo --version shows the require-able gem version"
|
158
|
+
puts
|
159
|
+
|
160
|
+
Kernel::exit( KERNEL_EXIT_FAILURE )
|
161
|
+
end
|
162
|
+
|
163
|
+
def show_version
|
164
|
+
require 'hoodoo/version'
|
165
|
+
|
166
|
+
puts
|
167
|
+
puts "Accessible Hoodoo gem is #{ Hoodoo::VERSION } (#{ Hoodoo::DATE })"
|
168
|
+
puts
|
129
169
|
|
130
170
|
Kernel::exit( KERNEL_EXIT_FAILURE )
|
131
171
|
end
|
@@ -37,22 +37,27 @@ module Hoodoo
|
|
37
37
|
# the response. It calls the original implementation via +super+.
|
38
38
|
#
|
39
39
|
# +http_message+:: Hash describing the message to send.
|
40
|
-
#
|
41
40
|
# +full_uri+:: URI being sent to. This is somewhat meaningless
|
42
|
-
# when using AMQP but
|
41
|
+
# when using AMQP but useful for analytics data.
|
43
42
|
#
|
44
43
|
def monkey_send_request( http_message, full_uri )
|
45
44
|
amqp_response = nil
|
46
45
|
|
47
46
|
Datadog.tracer.trace( 'alchemy.request' ) do |span|
|
48
|
-
span.service
|
47
|
+
span.service = 'alchemy'
|
49
48
|
span.span_type = 'alchemy'
|
50
|
-
span.resource
|
49
|
+
span.resource = http_message[ 'verb' ]
|
51
50
|
span.set_tag( 'target.path', http_message[ 'path'] )
|
52
51
|
|
53
|
-
# Add Datadog trace IDs to the HTTP message
|
52
|
+
# Add Datadog trace IDs to the HTTP message. For compatibility
|
53
|
+
# with Hoodoo V1 services using a fork of DDTrace, we send both
|
54
|
+
# old headers ("X-DDTrace...") and new ("X-DataDog-...")
|
55
|
+
|
56
|
+
http_message[ 'headers' ][ 'X_DATADOG_TRACE_ID' ] = span.trace_id.to_s
|
57
|
+
http_message[ 'headers' ][ 'X_DATADOG_PARENT_ID' ] = span.span_id.to_s
|
58
|
+
|
54
59
|
http_message[ 'headers' ][ 'X_DDTRACE_PARENT_TRACE_ID' ] = span.trace_id.to_s
|
55
|
-
http_message[ 'headers' ][ 'X_DDTRACE_PARENT_SPAN_ID'
|
60
|
+
http_message[ 'headers' ][ 'X_DDTRACE_PARENT_SPAN_ID' ] = span.span_id.to_s
|
56
61
|
|
57
62
|
amqp_response = super( http_message, full_uri )
|
58
63
|
end
|
@@ -13,7 +13,10 @@
|
|
13
13
|
########################################################################
|
14
14
|
|
15
15
|
begin
|
16
|
+
|
16
17
|
# Raises LoadError if NewRelic is absent
|
18
|
+
#
|
19
|
+
require 'newrelic_rpm'
|
17
20
|
require 'new_relic/agent/method_tracer'
|
18
21
|
|
19
22
|
# Add a method tracer on the dispatch method so that the time spent
|
@@ -29,6 +32,7 @@ begin
|
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
35
|
+
|
32
36
|
rescue LoadError; end
|
33
37
|
|
34
38
|
module Hoodoo
|
@@ -55,18 +59,19 @@ module Hoodoo
|
|
55
59
|
# Add custom attributes to the NewRelic transaction. The original
|
56
60
|
# implementation is called via +super+.
|
57
61
|
#
|
58
|
-
# +interaction+:: Hoodoo::Services::Interaction
|
59
|
-
# inbound request. The
|
60
|
-
# +rack_request+ and +session+
|
61
|
-
# latter being optional). If
|
62
|
-
# +requested_action+ are
|
63
|
-
# _might_ be logged according
|
64
|
-
# in the interface; if these
|
65
|
-
# data is _not_ logged.
|
62
|
+
# +interaction+:: Hoodoo::Services::Middleware::Interaction
|
63
|
+
# instance describing the inbound request. The
|
64
|
+
# +interaction_id+, +rack_request+ and +session+
|
65
|
+
# data is used (the latter being optional). If
|
66
|
+
# +target_interface+ and +requested_action+ are
|
67
|
+
# available, body data _might_ be logged according
|
68
|
+
# to secure log settings in the interface; if these
|
69
|
+
# values are unset, body data is _not_ logged.
|
66
70
|
#
|
67
71
|
def monkey_log_inbound_request( interaction )
|
68
72
|
|
69
73
|
# Add custom attributes to the NewRelic transaction.
|
74
|
+
#
|
70
75
|
::NewRelic::Agent.add_custom_attributes(
|
71
76
|
{
|
72
77
|
:target_action => interaction.requested_action,
|
@@ -75,6 +80,7 @@ module Hoodoo
|
|
75
80
|
)
|
76
81
|
|
77
82
|
# Call the original logging method.
|
83
|
+
#
|
78
84
|
super( interaction )
|
79
85
|
|
80
86
|
end
|
@@ -85,16 +91,17 @@ module Hoodoo
|
|
85
91
|
if defined?( Hoodoo::Services ) &&
|
86
92
|
defined?( Hoodoo::Services::Middleware )
|
87
93
|
|
88
|
-
|
94
|
+
Hoodoo::Monkey.register(
|
89
95
|
target_unit: Hoodoo::Services::Middleware,
|
90
96
|
extension_module: Hoodoo::Monkey::Patch::NewRelicMiddlewareAnalytics
|
91
97
|
)
|
92
98
|
|
93
|
-
|
99
|
+
Hoodoo::Monkey.enable( extension_module: Hoodoo::Monkey::Patch::NewRelicMiddlewareAnalytics )
|
94
100
|
end
|
95
101
|
|
96
102
|
rescue LoadError
|
97
103
|
# No NewRelic => do nothing
|
104
|
+
|
98
105
|
end
|
99
106
|
|
100
107
|
end # module Patch
|
@@ -16,7 +16,11 @@ module Hoodoo
|
|
16
16
|
module Patch
|
17
17
|
|
18
18
|
begin
|
19
|
-
|
19
|
+
|
20
|
+
# Raises LoadError if NewRelic is absent
|
21
|
+
#
|
22
|
+
require 'newrelic_rpm'
|
23
|
+
require 'new_relic/agent/transaction'
|
20
24
|
|
21
25
|
# Wrap Hoodoo::Client::Endpoint::AMQP using NewRelic transaction
|
22
26
|
# tracing so that over-queue inter-resource calls get connected
|
@@ -36,36 +40,47 @@ module Hoodoo
|
|
36
40
|
# This adds headers to the request and extracts header data from
|
37
41
|
# the response. It calls the original implementation via +super+.
|
38
42
|
#
|
39
|
-
# +http_message+:: Hash describing the message to send.
|
43
|
+
# +http_message+:: Hash describing the message to send. See e.g.
|
44
|
+
# Hoodoo::Client::Endpoint::AMQP#do_amqp. Note
|
45
|
+
# that the header names inside this Hash are the
|
46
|
+
# mixed case, HTTP specification style ones like
|
47
|
+
# <tt>X-Interaction-ID</tt> and _not_ the Rack
|
48
|
+
# names like <tt>HTTP_X_INTERACTION_ID</tt>.
|
40
49
|
#
|
41
50
|
# +full_uri+:: URI being sent to. This is somewhat meaningless
|
42
51
|
# when using AMQP but NewRelic requires it.
|
43
52
|
#
|
44
53
|
def monkey_send_request( http_message, full_uri )
|
45
|
-
amqp_response
|
46
|
-
|
54
|
+
amqp_response = nil
|
55
|
+
wrapped_request = AlchemyFluxHTTPRequestWrapper.new(
|
47
56
|
http_message,
|
48
57
|
full_uri
|
49
58
|
)
|
50
59
|
|
51
|
-
::NewRelic::Agent::
|
60
|
+
segment = ::NewRelic::Agent::Transaction.start_external_request_segment(
|
61
|
+
wrapped_request.type,
|
62
|
+
wrapped_request.uri,
|
63
|
+
wrapped_request.method
|
64
|
+
)
|
65
|
+
|
66
|
+
begin
|
67
|
+
segment.add_request_headers( wrapped_request )
|
68
|
+
|
69
|
+
amqp_response = super( http_message, full_uri )
|
52
70
|
|
53
|
-
#
|
54
|
-
#
|
71
|
+
# The outer block extracts required information from the
|
72
|
+
# object returned by this block. Need to wrap it match the
|
73
|
+
# expected interface.
|
55
74
|
#
|
56
|
-
|
75
|
+
wrapped_response = AlchemyFluxHTTPResponseWrapper.new(
|
76
|
+
amqp_response
|
77
|
+
)
|
57
78
|
|
58
|
-
|
79
|
+
segment.read_response_headers( wrapped_response )
|
59
80
|
|
60
|
-
|
61
|
-
|
62
|
-
# expected interface.
|
63
|
-
#
|
64
|
-
::Hoodoo::Monkey::Patch::NewRelicTracedAMQP::AMQPNewRelicResponseWrapper.new(
|
65
|
-
amqp_response
|
66
|
-
)
|
81
|
+
ensure
|
82
|
+
segment.finish()
|
67
83
|
|
68
|
-
end
|
69
84
|
end
|
70
85
|
|
71
86
|
return amqp_response
|
@@ -75,12 +90,18 @@ module Hoodoo
|
|
75
90
|
# Wrapper class for an AMQP request which conforms to the API that
|
76
91
|
# NewRelic expects.
|
77
92
|
#
|
78
|
-
class
|
93
|
+
class AlchemyFluxHTTPRequestWrapper
|
79
94
|
|
80
95
|
# Wrap the Alchemy Flux +http_message+ aimed at the specified
|
81
96
|
# +full_uri+.
|
82
97
|
#
|
83
|
-
# +http_message+:: Hash describing the
|
98
|
+
# +http_message+:: Hash describing the message to send. See e.g.
|
99
|
+
# Hoodoo::Client::Endpoint::AMQP#do_amqp. Note
|
100
|
+
# that the header names inside this Hash are the
|
101
|
+
# mixed case, HTTP specification style ones like
|
102
|
+
# <tt>X-Interaction-ID</tt> and _not_ the Rack
|
103
|
+
# names like <tt>HTTP_X_INTERACTION_ID</tt>.
|
104
|
+
#
|
84
105
|
# +full_uri+:: Full target URI, as a String.
|
85
106
|
#
|
86
107
|
def initialize( http_message, full_uri )
|
@@ -91,13 +112,28 @@ module Hoodoo
|
|
91
112
|
# String describing what kind of request this is.
|
92
113
|
#
|
93
114
|
def type
|
94
|
-
|
115
|
+
'AlchemyFlux'
|
116
|
+
end
|
117
|
+
|
118
|
+
# String descrbing this request's intended host, according to the
|
119
|
+
# +Host+ header. May return +nil+ if none is found.
|
120
|
+
#
|
121
|
+
# See also: #host.
|
122
|
+
#
|
123
|
+
def host_from_header
|
124
|
+
begin
|
125
|
+
@http_message[ 'headers' ][ 'host' ] || @http_message[ 'headers' ][ 'Host' ]
|
126
|
+
rescue
|
127
|
+
nil
|
128
|
+
end
|
95
129
|
end
|
96
130
|
|
97
131
|
# String describing this request's intended host.
|
98
132
|
#
|
133
|
+
# See also: #host_from_header.
|
134
|
+
#
|
99
135
|
def host
|
100
|
-
@http_message[ 'host' ]
|
136
|
+
self.host_from_header() || @http_message[ 'host' ]
|
101
137
|
end
|
102
138
|
|
103
139
|
# String describing this request's HTTP verb (GET, POST and
|
@@ -127,10 +163,10 @@ module Hoodoo
|
|
127
163
|
@http_message[ 'headers' ][ key ] = value
|
128
164
|
end
|
129
165
|
|
130
|
-
#
|
166
|
+
# URI object describing the full request URI.
|
131
167
|
#
|
132
168
|
def uri
|
133
|
-
@full_uri
|
169
|
+
URI.parse( @full_uri.to_s )
|
134
170
|
end
|
135
171
|
|
136
172
|
end
|
@@ -138,29 +174,30 @@ module Hoodoo
|
|
138
174
|
# Wrapper class for an AMQP request which conforms to the API that
|
139
175
|
# NewRelic expects.
|
140
176
|
#
|
141
|
-
class
|
177
|
+
class AlchemyFluxHTTPResponseWrapper
|
142
178
|
|
143
179
|
# The +response_hash+ to be wrapped.
|
144
180
|
#
|
145
181
|
# +response_hash+:: Hash describing the response returned from
|
146
|
-
# Alchemy Flux.
|
182
|
+
# Alchemy Flux. See that gem for details.
|
147
183
|
#
|
148
184
|
def initialize( response_hash )
|
149
185
|
@response_hash = response_hash
|
150
186
|
end
|
151
187
|
|
152
|
-
#
|
153
|
-
#
|
154
|
-
# key like normal.
|
188
|
+
# Look up a key in the headers Hash first, but if absent try the
|
189
|
+
# top-level response Hash instead.
|
155
190
|
#
|
156
191
|
# +key+:: Hash key to look up.
|
157
192
|
#
|
158
193
|
def []( key )
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
194
|
+
@response_hash[ 'headers' ][ key ] || @response_hash[ key ]
|
195
|
+
end
|
196
|
+
|
197
|
+
# Return the HTTP headers for this response as a Hash.
|
198
|
+
#
|
199
|
+
def to_hash
|
200
|
+
@response_hash[ 'headers' ].dup()
|
164
201
|
end
|
165
202
|
end
|
166
203
|
end
|
@@ -185,6 +222,7 @@ module Hoodoo
|
|
185
222
|
|
186
223
|
rescue LoadError
|
187
224
|
# No NewRelic => do nothing
|
225
|
+
|
188
226
|
end
|
189
227
|
|
190
228
|
end # module Patch
|