honeycomb-beeline 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +72 -0
- data/Appraisals +8 -0
- data/CONTRIBUTORS.md +2 -1
- data/Gemfile.lock +8 -4
- data/README.md +1 -0
- data/lib/honeycomb-beeline.rb +1 -0
- data/lib/honeycomb/beeline/version.rb +1 -1
- data/lib/honeycomb/integrations/aws.rb +396 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 579452ea20fce15d631f43ca535105440ec154e23964e0639ab11d9f81ca6e3d
|
4
|
+
data.tar.gz: 2497fda72bbbc1f9b0957e17a551677989a2b0b50e30a3356218632b64f47fc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b72e001d1217f09608754912114f7d82d0d712fccf1366da8bf299108f408b2fde3721f061975311768732e98693381ce0c0228fec3230ebecb2c7f2ee01313
|
7
|
+
data.tar.gz: 2179d51cdaab95f132dde4d2570071b0042bb9c7472e2b1de3243db89b54329c99a4fcd7207c226e398742712b6c868854ce5566997597ffcafeed9798ef41e5
|
data/.circleci/config.yml
CHANGED
@@ -45,6 +45,14 @@ commands:
|
|
45
45
|
- run: BUNDLE_GEMFILE=<< parameters.gemfile >> << parameters.command >>
|
46
46
|
|
47
47
|
gemfiles:
|
48
|
+
aws-two: &aws-two
|
49
|
+
steps:
|
50
|
+
- ruby:
|
51
|
+
gemfile: gemfiles/aws_2.gemfile
|
52
|
+
aws-three: &aws-three
|
53
|
+
steps:
|
54
|
+
- ruby:
|
55
|
+
gemfile: gemfiles/aws_3.gemfile
|
48
56
|
faraday: &faraday
|
49
57
|
steps:
|
50
58
|
- ruby:
|
@@ -105,6 +113,30 @@ jobs:
|
|
105
113
|
steps:
|
106
114
|
- ruby:
|
107
115
|
command: bundle exec rake rubocop
|
116
|
+
aws-two-ruby-two-three:
|
117
|
+
<<: *aws-two
|
118
|
+
executor: ruby-two-three
|
119
|
+
aws-two-ruby-two-four:
|
120
|
+
<<: *aws-two
|
121
|
+
executor: ruby-two-four
|
122
|
+
aws-two-ruby-two-five:
|
123
|
+
<<: *aws-two
|
124
|
+
executor: ruby-two-five
|
125
|
+
aws-two-ruby-two-six:
|
126
|
+
<<: *aws-two
|
127
|
+
executor: ruby-two-six
|
128
|
+
aws-three-ruby-two-three:
|
129
|
+
<<: *aws-three
|
130
|
+
executor: ruby-two-three
|
131
|
+
aws-three-ruby-two-four:
|
132
|
+
<<: *aws-three
|
133
|
+
executor: ruby-two-four
|
134
|
+
aws-three-ruby-two-five:
|
135
|
+
<<: *aws-three
|
136
|
+
executor: ruby-two-five
|
137
|
+
aws-three-ruby-two-six:
|
138
|
+
<<: *aws-three
|
139
|
+
executor: ruby-two-six
|
108
140
|
faraday-ruby-two-three:
|
109
141
|
<<: *faraday
|
110
142
|
executor: ruby-two-three
|
@@ -243,6 +275,38 @@ workflows:
|
|
243
275
|
beeline:
|
244
276
|
jobs:
|
245
277
|
- lint: *tag_filters
|
278
|
+
- aws-two-ruby-two-three:
|
279
|
+
<<: *tag_filters
|
280
|
+
requires:
|
281
|
+
- lint
|
282
|
+
- aws-two-ruby-two-four:
|
283
|
+
<<: *tag_filters
|
284
|
+
requires:
|
285
|
+
- lint
|
286
|
+
- aws-two-ruby-two-five:
|
287
|
+
<<: *tag_filters
|
288
|
+
requires:
|
289
|
+
- lint
|
290
|
+
- aws-two-ruby-two-six:
|
291
|
+
<<: *tag_filters
|
292
|
+
requires:
|
293
|
+
- lint
|
294
|
+
- aws-three-ruby-two-three:
|
295
|
+
<<: *tag_filters
|
296
|
+
requires:
|
297
|
+
- lint
|
298
|
+
- aws-three-ruby-two-four:
|
299
|
+
<<: *tag_filters
|
300
|
+
requires:
|
301
|
+
- lint
|
302
|
+
- aws-three-ruby-two-five:
|
303
|
+
<<: *tag_filters
|
304
|
+
requires:
|
305
|
+
- lint
|
306
|
+
- aws-three-ruby-two-six:
|
307
|
+
<<: *tag_filters
|
308
|
+
requires:
|
309
|
+
- lint
|
246
310
|
- faraday-ruby-two-three:
|
247
311
|
<<: *tag_filters
|
248
312
|
requires:
|
@@ -407,6 +471,14 @@ workflows:
|
|
407
471
|
ignore: /.*/
|
408
472
|
requires:
|
409
473
|
- lint
|
474
|
+
- aws-two-ruby-two-three
|
475
|
+
- aws-two-ruby-two-four
|
476
|
+
- aws-two-ruby-two-five
|
477
|
+
- aws-two-ruby-two-six
|
478
|
+
- aws-three-ruby-two-three
|
479
|
+
- aws-three-ruby-two-four
|
480
|
+
- aws-three-ruby-two-five
|
481
|
+
- aws-three-ruby-two-six
|
410
482
|
- faraday-ruby-two-three
|
411
483
|
- faraday-ruby-two-four
|
412
484
|
- faraday-ruby-two-five
|
data/Appraisals
CHANGED
data/CONTRIBUTORS.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
honeycomb-beeline (1.
|
4
|
+
honeycomb-beeline (1.2.0)
|
5
5
|
libhoney (~> 1.8)
|
6
6
|
|
7
7
|
GEM
|
@@ -27,16 +27,20 @@ GEM
|
|
27
27
|
domain_name (0.5.20190701)
|
28
28
|
unf (>= 0.0.5, < 1.0.0)
|
29
29
|
ffi (1.11.1)
|
30
|
+
ffi-compiler (1.0.1)
|
31
|
+
ffi (>= 1.0.0)
|
32
|
+
rake
|
30
33
|
hashdiff (0.4.0)
|
31
|
-
http (4.
|
34
|
+
http (4.2.0)
|
32
35
|
addressable (~> 2.3)
|
33
36
|
http-cookie (~> 1.0)
|
34
37
|
http-form_data (~> 2.0)
|
35
|
-
|
38
|
+
http-parser (~> 1.2.0)
|
36
39
|
http-cookie (1.0.3)
|
37
40
|
domain_name (~> 0.5)
|
38
41
|
http-form_data (2.1.1)
|
39
|
-
|
42
|
+
http-parser (1.2.1)
|
43
|
+
ffi-compiler (>= 1.0, < 2.0)
|
40
44
|
iniparse (1.4.4)
|
41
45
|
jaro_winkler (1.5.3)
|
42
46
|
json (2.2.0)
|
data/README.md
CHANGED
data/lib/honeycomb-beeline.rb
CHANGED
@@ -0,0 +1,396 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "aws-sdk-core"
|
4
|
+
|
5
|
+
module Honeycomb
|
6
|
+
module Aws
|
7
|
+
# The version of the aws-sdk-core gem.
|
8
|
+
#
|
9
|
+
# Aws::VERSION was removed in aws-sdk-core v3.2.1 in favor of
|
10
|
+
# Aws::CORE_GEM_VERSION. However, it's still present in aws-sdk v2.
|
11
|
+
#
|
12
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/d0c5f6e5a3e83eeda2d1c81f5dd80e5ac562a6dc/gems/aws-sdk-core/CHANGELOG.md#321-2017-09-06
|
13
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/4c40f6e67e763a0f392ba5b1449254426b68a600/aws-sdk-core/lib/aws-sdk-core/version.rb#L2
|
14
|
+
SDK_VERSION =
|
15
|
+
defined?(::Aws::VERSION) ? ::Aws::VERSION : ::Aws::CORE_GEM_VERSION
|
16
|
+
|
17
|
+
# Instruments AWS clients with Honeycomb events.
|
18
|
+
#
|
19
|
+
# This plugin is automatically added to any aws-sdk client class your app
|
20
|
+
# uses. It uses {SdkHandler} to wrap a Honeycomb span around each
|
21
|
+
# invocation of a client object method. Within that span, there is
|
22
|
+
# typically at least one actual HTTP API call to Amazon, so each API call
|
23
|
+
# is wrapped in a separate span by {ApiHandler}.
|
24
|
+
#
|
25
|
+
# This plugin adds the following options which you can use to configure
|
26
|
+
# your aws-sdk clients:
|
27
|
+
#
|
28
|
+
# * `:honeycomb` - Boolean indicating whether to enable Honeycomb
|
29
|
+
# instrumentation. Defaults to `true`.
|
30
|
+
#
|
31
|
+
# * `:honeycomb_client` - Allows you to set a custom Honeycomb client
|
32
|
+
# object. Defaults to the global {Honeycomb.client}.
|
33
|
+
#
|
34
|
+
# @example Disable the Honeycomb AWS integration globally.
|
35
|
+
# Aws.config.update(honeycomb: false)
|
36
|
+
#
|
37
|
+
# @example Disable the Honeycomb AWS integration locally.
|
38
|
+
# s3 = Aws::S3::Client.new(honeycomb: false)
|
39
|
+
#
|
40
|
+
# @example Use a different client globally.
|
41
|
+
# Aws.config.update(honeycomb_client: custom)
|
42
|
+
#
|
43
|
+
# @example Use a different client locally.
|
44
|
+
# dynamodb = Aws::DynamoDB::Client.new(honeycomb_client: custom)
|
45
|
+
#
|
46
|
+
# @see https://docs.aws.amazon.com/sdk-for-ruby/v3/api/index.html#Configuration_Options
|
47
|
+
# @see SdkHandler
|
48
|
+
# @see ApiHandler
|
49
|
+
class Plugin < Seahorse::Client::Plugin
|
50
|
+
option(
|
51
|
+
:honeycomb,
|
52
|
+
default: true,
|
53
|
+
doc_type: "Boolean",
|
54
|
+
docstring: "When `true`, emit Honeycomb events for every SDK/API call.",
|
55
|
+
)
|
56
|
+
|
57
|
+
option(
|
58
|
+
:honeycomb_client,
|
59
|
+
doc_type: Honeycomb::Client,
|
60
|
+
docstring: "The Honeycomb client used for sending SDK/API call events.",
|
61
|
+
) { Honeycomb.client }
|
62
|
+
|
63
|
+
def add_handlers(handlers, config)
|
64
|
+
return unless config.honeycomb && config.honeycomb_client
|
65
|
+
|
66
|
+
handlers.add(SdkHandler, step: :initialize)
|
67
|
+
handlers.add(ApiHandler, step: :sign, priority: 39)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# An AWS plugin handler that creates spans around SDK calls.
|
72
|
+
#
|
73
|
+
# Each aws-sdk client provides a one-to-one mapping of methods to logical
|
74
|
+
# API operations. {SdkHandler} is responsible for starting the root level
|
75
|
+
# AWS span for the SDK method being called.
|
76
|
+
#
|
77
|
+
# {Plugin} accomplishes this by adding the handler early in the process,
|
78
|
+
# around the initialization step. This doesn't necessarily represent the
|
79
|
+
# network latency of the API requests being made to Amazon, since SDK calls
|
80
|
+
# might involve several underlying operations: request signing, response
|
81
|
+
# parsing, retries, redirects, etc. Thus, {ApiHandler} is responsible for
|
82
|
+
# creating child spans around individual HTTP API calls. The span created
|
83
|
+
# by {SdkHandler} represents the overall result of the method call you made
|
84
|
+
# to the aws-sdk client.
|
85
|
+
class SdkHandler < Seahorse::Client::Handler
|
86
|
+
def call(context)
|
87
|
+
setup(context)
|
88
|
+
response = @handler.call(context)
|
89
|
+
teardown(context, response.error)
|
90
|
+
response
|
91
|
+
rescue StandardError => e
|
92
|
+
teardown(context, e)
|
93
|
+
raise e
|
94
|
+
ensure
|
95
|
+
finish(context)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def setup(context)
|
101
|
+
span = context.config.honeycomb_client.start_span(name: "aws-sdk")
|
102
|
+
context[:honeycomb_aws_sdk_span] = span
|
103
|
+
context[:honeycomb_aws_sdk_data] = {
|
104
|
+
"meta.package" => context[:gem_name] || "aws-sdk",
|
105
|
+
"meta.package_version" => context[:gem_version] || SDK_VERSION,
|
106
|
+
"aws.region" => context.config.region,
|
107
|
+
"aws.service" => context.client.class.identifier,
|
108
|
+
"aws.operation" => context.operation_name,
|
109
|
+
"aws.params" => context.params,
|
110
|
+
}
|
111
|
+
span.add context[:honeycomb_aws_sdk_data]
|
112
|
+
end
|
113
|
+
|
114
|
+
def teardown(context, error)
|
115
|
+
span = context[:honeycomb_aws_sdk_span]
|
116
|
+
span.add_field "aws.request_id", context[:request_id]
|
117
|
+
span.add_field "aws.retries", context.retries
|
118
|
+
span.add_field "aws.retry_limit", context.config.retry_limit
|
119
|
+
span.add_field "aws.error", error.class.name if error
|
120
|
+
span.add_field "aws.error_detail", error.message if error
|
121
|
+
end
|
122
|
+
|
123
|
+
def finish(context)
|
124
|
+
span = context.metadata.delete(:honeycomb_aws_sdk_span)
|
125
|
+
span.send
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# An AWS plugin handler that creates spans around API calls.
|
130
|
+
#
|
131
|
+
# Each aws-sdk client provides a one-to-one mapping of methods to API
|
132
|
+
# operations. However, this doesn't mean that each method results in only
|
133
|
+
# one HTTP request to Amazon's servers. There may be request errors,
|
134
|
+
# retries, redirects, etc. So whereas {SdkHandler} wraps the logical
|
135
|
+
# operation in a span, {ApiHandler} wraps the individual API requests in
|
136
|
+
# separate child spans.
|
137
|
+
#
|
138
|
+
# {Plugin} accomplishes this by adding {ApiHandler} as close to sending as
|
139
|
+
# possible, before the client retries requests, follows redirects, or even
|
140
|
+
# parses out response errors. That way, a new span is created for every
|
141
|
+
# literal HTTP request. But it also means we have to take care to propagate
|
142
|
+
# error information to the span correctly, since the stock AWS error
|
143
|
+
# handlers are upstream from this one.
|
144
|
+
#
|
145
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/767a96db5cb98424a78249dca3f0be802148372e/gems/aws-sdk-s3/lib/aws-sdk-s3/plugins/s3_signer.rb#L36
|
146
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/767a96db5cb98424a78249dca3f0be802148372e/gems/aws-sdk-core/lib/aws-sdk-core/plugins/client_metrics_send_plugin.rb#L9-L11
|
147
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/97b28ccf18558fc908fd56f52741cf3329de9869/gems/aws-sdk-core/lib/seahorse/client/plugins/raise_response_errors.rb
|
148
|
+
class ApiHandler < Seahorse::Client::Handler
|
149
|
+
def call(context)
|
150
|
+
context.config.honeycomb_client.start_span(name: "aws-api") do |span|
|
151
|
+
instrument(span, context)
|
152
|
+
@handler.call(context)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def instrument(span, context)
|
159
|
+
context[:honeycomb_aws_api_span] = span
|
160
|
+
handle_request(context)
|
161
|
+
handle_response(context)
|
162
|
+
end
|
163
|
+
|
164
|
+
def handle_request(context)
|
165
|
+
span = context[:honeycomb_aws_api_span]
|
166
|
+
add_aws_api_fields(span, context)
|
167
|
+
add_request_fields(span, context)
|
168
|
+
end
|
169
|
+
|
170
|
+
def add_aws_api_fields(span, context)
|
171
|
+
span.add context[:honeycomb_aws_sdk_data]
|
172
|
+
span.add_field "aws.attempt", context.retries + 1
|
173
|
+
add_credentials(span, context) if context.config.credentials
|
174
|
+
handle_redirect(span, context) if context[:redirect_region]
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_credentials(span, context)
|
178
|
+
credentials = context.config.credentials.credentials
|
179
|
+
span.add_field "aws.access_key_id", credentials.access_key_id
|
180
|
+
span.add_field "aws.session_token", credentials.session_token
|
181
|
+
end
|
182
|
+
|
183
|
+
def handle_redirect(span, context)
|
184
|
+
span.add_field "aws.region", context[:redirect_region]
|
185
|
+
end
|
186
|
+
|
187
|
+
def add_request_fields(span, context)
|
188
|
+
request = context.http_request
|
189
|
+
span.add_field "request.method", request.http_method
|
190
|
+
span.add_field "request.scheme", request.endpoint.scheme
|
191
|
+
span.add_field "request.host", request.endpoint.host
|
192
|
+
span.add_field "request.path", request.endpoint.path
|
193
|
+
span.add_field "request.query", request.endpoint.query
|
194
|
+
span.add_field "request.user_agent", request.headers["user-agent"]
|
195
|
+
end
|
196
|
+
|
197
|
+
def handle_response(context)
|
198
|
+
on_headers(context)
|
199
|
+
on_error(context)
|
200
|
+
on_done(context)
|
201
|
+
end
|
202
|
+
|
203
|
+
def on_headers(context)
|
204
|
+
context.http_response.on_headers do |status_code, headers|
|
205
|
+
span = context[:honeycomb_aws_api_span]
|
206
|
+
span.add_field "response.status_code", status_code
|
207
|
+
headers.each do |header, value|
|
208
|
+
if header.start_with?("x-amz-", "x-amzn-")
|
209
|
+
field = "response.#{header.tr('-', '_')}"
|
210
|
+
span.add_field(field, value)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def on_error(context)
|
217
|
+
context.http_response.on_error do |error|
|
218
|
+
span = context[:honeycomb_aws_api_span]
|
219
|
+
span.add_field "response.error", error.class.name
|
220
|
+
span.add_field "response.error_detail", error.message
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def on_done(context)
|
225
|
+
context.http_response.on_done(300..599) do
|
226
|
+
process_api_error(context)
|
227
|
+
process_s3_region(context)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def process_api_error(context)
|
232
|
+
span = context[:honeycomb_aws_api_span]
|
233
|
+
error = parse_api_error_from(context)
|
234
|
+
add_api_error_fields(span, error) if error
|
235
|
+
end
|
236
|
+
|
237
|
+
def add_api_error_fields(span, error)
|
238
|
+
span.add_field "response.error", error.code
|
239
|
+
span.add_field "response.error_detail", error.message
|
240
|
+
end
|
241
|
+
|
242
|
+
# Runs a limited subset of response parsing for AWS-specific errors.
|
243
|
+
#
|
244
|
+
# Because XML/JSON error handlers are inserted at priority 50 of the
|
245
|
+
# :sign step, they're upstream from {ApiHandler} (at priority 39), so we
|
246
|
+
# won't have access to the error saved in Seahorse::Client::Response yet.
|
247
|
+
# We only have the error in the Seahorse::Client::Http::Response object.
|
248
|
+
# But Seahorse::Client::NetHttp::Handler only triggers an HTTP response
|
249
|
+
# error for rescued exceptions (e.g., timeouts). We might still get back
|
250
|
+
# successful HTTP 3xx, 4xx, or 5xx responses that should be interpreted
|
251
|
+
# as aws-api errors.
|
252
|
+
#
|
253
|
+
# So we have to duplicate the logic of either Aws::Xml::ErrorHandler or
|
254
|
+
# Aws::Json::ErrorHandler depending on which one is being used by the
|
255
|
+
# current client. We can determine this by their "protocol" metadata.
|
256
|
+
#
|
257
|
+
# Note that there are still a few straggling errors that might occur from
|
258
|
+
# HTTP 2xx responses. Since those aren't really API call failures, we
|
259
|
+
# won't worry about parsing them out for the aws-api span. Once the
|
260
|
+
# upstream handlers process those errors, they'll be propagated to the
|
261
|
+
# aws-sdk span anyway (since {SdkHandler} will actually have access to
|
262
|
+
# the Seahorse::Client::Response#error).
|
263
|
+
#
|
264
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/d0c5f6e5a3e83eeda2d1c81f5dd80e5ac562a6dc/gems/aws-sdk-core/lib/aws-sdk-core/client_stubs.rb#L298-L307
|
265
|
+
# @see https://github.com/aws/aws-sdk-ruby/tree/b0ade445ce18b24c53a4548074b214e732b8b627/gems/aws-sdk-core/lib/aws-sdk-core/plugins/protocols
|
266
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/354d36792e47f2e81b4889f322928e848e062818/gems/aws-sdk-s3/lib/aws-sdk-s3/plugins/http_200_errors.rb
|
267
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/354d36792e47f2e81b4889f322928e848e062818/gems/aws-sdk-dynamodb/lib/aws-sdk-dynamodb/plugins/crc32_validation.rb
|
268
|
+
def parse_api_error_from(context)
|
269
|
+
case context.config.api.metadata["protocol"]
|
270
|
+
when "query", "rest-xml", "ec2"
|
271
|
+
XmlError.new(context)
|
272
|
+
when "json", "rest-json"
|
273
|
+
JsonError.new(context)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# @private
|
278
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/d0c5f6e5a3e83eeda2d1c81f5dd80e5ac562a6dc/gems/aws-sdk-core/lib/aws-sdk-core/xml/error_handler.rb
|
279
|
+
class XmlError < ::Aws::Xml::ErrorHandler
|
280
|
+
attr_reader :code, :message
|
281
|
+
|
282
|
+
def initialize(context)
|
283
|
+
body = context.http_response.body_contents
|
284
|
+
@code = error_code(body, context)
|
285
|
+
@message = error_message(body)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# @private
|
290
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/d0c5f6e5a3e83eeda2d1c81f5dd80e5ac562a6dc/gems/aws-sdk-core/lib/aws-sdk-core/json/error_handler.rb
|
291
|
+
class JsonError < ::Aws::Json::ErrorHandler
|
292
|
+
attr_reader :code, :message
|
293
|
+
|
294
|
+
def initialize(context)
|
295
|
+
body = context.http_response.body_contents
|
296
|
+
json = ::Aws::Json.load(body) || {}
|
297
|
+
@code = error_code(json, context)
|
298
|
+
@message = error_message(code, json)
|
299
|
+
rescue ::Aws::Json::ParseError
|
300
|
+
@code = http_status_error_code(context)
|
301
|
+
@message = ""
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Propagates S3 region redirect information to the next aws-api span.
|
306
|
+
#
|
307
|
+
# When the AWS S3 client is configured with the wrong region, Amazon
|
308
|
+
# responds to API requests with an HTTP 400 indicating the correct region
|
309
|
+
# for the bucket.
|
310
|
+
#
|
311
|
+
# This error is normally caught upstream by stock plugins that trigger a
|
312
|
+
# new API request with the right region, which will create another
|
313
|
+
# aws-api span after this one. However, since the aws.region field set by
|
314
|
+
# {#add_aws_api_fields} comes from the aws-sdk configuration, its value
|
315
|
+
# would continue being wrong in the next aws-api span. Instead, we want
|
316
|
+
# this span to have the wrong region (that triggered the error) and the
|
317
|
+
# next span to have the right region (which won't come from the config).
|
318
|
+
#
|
319
|
+
# To update aws.region to the right value, {#handle_redirect} looks for a
|
320
|
+
# value stashed in the Seahorse::Client::Context#metadata. This is set by
|
321
|
+
# aws-sdk v3 (via the aws-sdk-s3 gem) but not by aws-sdk v2. So, we have
|
322
|
+
# to duplicate some of the upstream v3 logic in order to propagate the
|
323
|
+
# redirected region in the v2 case. We only do this in the v2 case in the
|
324
|
+
# hopes that eventually we don't have to maintain the duplicated logic.
|
325
|
+
#
|
326
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/379d338406873b0f4b53f118c83fe40761e297ab/gems/aws-sdk-s3/lib/aws-sdk-s3/plugins/s3_signer.rb#L151
|
327
|
+
def process_s3_region(context)
|
328
|
+
return unless SDK_VERSION.start_with?("2.")
|
329
|
+
|
330
|
+
redirect = S3Redirect.new(context)
|
331
|
+
context[:redirect_region] = redirect.region if redirect.happening?
|
332
|
+
end
|
333
|
+
|
334
|
+
# @private
|
335
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/379d338406873b0f4b53f118c83fe40761e297ab/gems/aws-sdk-s3/lib/aws-sdk-s3/plugins/s3_signer.rb#L102-L182
|
336
|
+
# @see https://github.com/aws/aws-sdk-ruby/blob/4c40f6e67e763a0f392ba5b1449254426b68a600/aws-sdk-core/lib/aws-sdk-core/plugins/s3_request_signer.rb#L81-L153
|
337
|
+
class S3Redirect
|
338
|
+
REGION_TAG = %r{<Region>(.+?)</Region>}.freeze
|
339
|
+
|
340
|
+
def initialize(context)
|
341
|
+
@context = context
|
342
|
+
end
|
343
|
+
|
344
|
+
def original_host
|
345
|
+
@context.http_request.endpoint.host
|
346
|
+
end
|
347
|
+
|
348
|
+
def status
|
349
|
+
@context.http_response.status_code
|
350
|
+
end
|
351
|
+
|
352
|
+
def happening?
|
353
|
+
status == 400 && region && !original_host.include?("fips")
|
354
|
+
end
|
355
|
+
|
356
|
+
def region
|
357
|
+
@region ||= region_from_headers || region_from_body
|
358
|
+
end
|
359
|
+
|
360
|
+
def region_from_headers
|
361
|
+
@context.http_response.headers["x-amz-bucket-region"]
|
362
|
+
end
|
363
|
+
|
364
|
+
def region_from_body
|
365
|
+
body = @context.http_response.body_contents
|
366
|
+
body.match(REGION_TAG) { |tag| tag[1] }
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
# Add the plugin to all aws-sdk client classes.
|
374
|
+
#
|
375
|
+
# Since client classes are dynamically created at load time by
|
376
|
+
# Seahorse::Client::Base.define, it's necessary to call .add_plugin on each
|
377
|
+
# class individually. For example, say we required aws-sdk-s3 before loading
|
378
|
+
# honeycomb-beeline. Then Aws::S3::Client would have already been defined
|
379
|
+
# without knowing about our plugin. So adding Honeycomb::Aws::Plugin to just
|
380
|
+
# Seahorse::Client::Base is insufficent. We have to call
|
381
|
+
# Aws::S3::Client.add_plugin as well.
|
382
|
+
#
|
383
|
+
# This loop will still add the plugin to Seahorse::Client::Base, which covers
|
384
|
+
# us if/when any future aws-sdk client classes get defined.
|
385
|
+
#
|
386
|
+
# This loop will *not* patch any instances of client objects that were created
|
387
|
+
# prior to loading honeycomb-beeline. While we could loop through
|
388
|
+
# ObjectSpace.each_object(Seahorse::Client::Base), it's much more awkward to
|
389
|
+
# reinitialize an existing instance. E.g., at the time the client instance was
|
390
|
+
# created, its configuration wouldn't have responded to the options defined by
|
391
|
+
# Honeycomb::Aws::Plugin, so we can't retroactively configure the plugin. In
|
392
|
+
# practice, this probably isn't a big deal: you'll likely load aws-sdk +
|
393
|
+
# honeycomb-beeline via bundler before ever instantiating an AWS client object.
|
394
|
+
ObjectSpace.each_object(Seahorse::Client::Base.singleton_class) do |client|
|
395
|
+
client.add_plugin(Honeycomb::Aws::Plugin)
|
396
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: honeycomb-beeline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Holman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: libhoney
|
@@ -227,6 +227,7 @@ files:
|
|
227
227
|
- lib/honeycomb/context.rb
|
228
228
|
- lib/honeycomb/deterministic_sampler.rb
|
229
229
|
- lib/honeycomb/integrations/active_support.rb
|
230
|
+
- lib/honeycomb/integrations/aws.rb
|
230
231
|
- lib/honeycomb/integrations/faraday.rb
|
231
232
|
- lib/honeycomb/integrations/rack.rb
|
232
233
|
- lib/honeycomb/integrations/rails.rb
|