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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6af6ff636f882a61957e48a0fe6fb50794d82b237f75009f5f2cc1308dbdafb
4
- data.tar.gz: cb2e735d0bb798150f7232739a9aced2dc372ce8dde81414925cf61b4259be3b
3
+ metadata.gz: 579452ea20fce15d631f43ca535105440ec154e23964e0639ab11d9f81ca6e3d
4
+ data.tar.gz: 2497fda72bbbc1f9b0957e17a551677989a2b0b50e30a3356218632b64f47fc8
5
5
  SHA512:
6
- metadata.gz: 0fb178a760b813ff7f26aa4a60650b7d9a1556041366fc65162b3132ec8926126aa066056329f49eaacbafacf5e33b559e740464a7957c48e54ecb071433536a
7
- data.tar.gz: 29c941bffdcdc5766de32665b87ad6d705f57f17c86dbdb75581519f480c2d47bfdba57a5640531b7ab95bf28730c33fd0f65b168e18295b3a9848bc5e954ba6
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
@@ -1,5 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ appraise "aws-2" do
4
+ gem "aws-sdk", "~> 2"
5
+ end
6
+
7
+ appraise "aws-3" do
8
+ gem "aws-sdk", "~> 3"
9
+ end
10
+
3
11
  appraise "faraday" do
4
12
  gem "faraday"
5
13
  end
data/CONTRIBUTORS.md CHANGED
@@ -1,3 +1,4 @@
1
1
  # Contributors
2
2
 
3
- Sergio Mira
3
+ * Sergio Mira
4
+ * Alex Vondrak - [@ajvondrak](https://github.com/ajvondrak)
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- honeycomb-beeline (1.1.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.1.1)
34
+ http (4.2.0)
32
35
  addressable (~> 2.3)
33
36
  http-cookie (~> 1.0)
34
37
  http-form_data (~> 2.0)
35
- http_parser.rb (~> 0.6.0)
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
- http_parser.rb (0.6.0)
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
@@ -16,6 +16,7 @@ Requires Ruby version 2.3 or later
16
16
  Built in instrumentation for:
17
17
 
18
18
  - Active Support
19
+ - AWS (v2 and v3)
19
20
  - Faraday
20
21
  - Rack
21
22
  - Rails (tested on versions 4.1 and up)
@@ -10,6 +10,7 @@ require "honeycomb/trace"
10
10
  module Honeycomb
11
11
  INTEGRATIONS = %i[
12
12
  active_support
13
+ aws
13
14
  faraday
14
15
  rack
15
16
  rails
@@ -3,7 +3,7 @@
3
3
  module Honeycomb
4
4
  module Beeline
5
5
  NAME = "honeycomb-beeline".freeze
6
- VERSION = "1.1.1".freeze
6
+ VERSION = "1.2.0".freeze
7
7
  USER_AGENT_SUFFIX = "#{NAME}/#{VERSION}".freeze
8
8
  end
9
9
  end
@@ -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.1.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-10-10 00:00:00.000000000 Z
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