hutch 0.24.0 → 0.25.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb197fc767359bc60d35790ec650c9c077120d0d
4
- data.tar.gz: e1152a8d434fdc9a4752c095ad5fdce4871ffd9e
3
+ metadata.gz: 6f166e0937fb1f468d18247afad90ff52263de57
4
+ data.tar.gz: 97b3c87cd087984409fda36f620c180a71df03d8
5
5
  SHA512:
6
- metadata.gz: c34020a830413a6e970bfae9e59fecdaebbe770aaf7df753a7f263af6d7dc98b0ae4433a4aafc27d9d8f45689168d0b39a64d3ba4086c05187fc7b0e352d9814
7
- data.tar.gz: cc24d3e6ab11c4979455c06ba057760b00cffae9dd577a39f4f89bfe7b45a33378c2190362c2d003a5418b3e64238d2997610c8b4f8f52a5287d0765563109ad
6
+ metadata.gz: 549463d55e4bfa5ee9b112c42a06c98999c25ab14a8562ef41adc75888f8eba30a42ebe39097ec7366939ad0ef44e38d6ffab8103f6f739f7e2aad68e955488c
7
+ data.tar.gz: 55972b247cb63587772adc3f12d40e852d6332c9991e08ddba7c388a4ab711851d81e3399b8d0c7407c2a16584f20f8c59aaede191f9eb41cd1d46208a0acb29
data/.travis.yml CHANGED
@@ -1,16 +1,13 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  before_install:
4
- - gem update --system
5
4
  - gem install bundler
6
5
  matrix:
7
6
  include:
8
- - rvm: 2.4.0
9
- - rvm: 2.3.3
10
- - rvm: 2.2
11
- - rvm: 2.1
12
- - rvm: 2.0
13
- - rvm: jruby-9.1.7.0
7
+ - rvm: 2.4.2
8
+ - rvm: 2.3.5
9
+ - rvm: 2.2.8
10
+ - rvm: jruby-9.1.15.0
14
11
  jdk: oraclejdk8
15
12
  env:
16
13
  - JRUBY_OPTS='--debug'
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
- ## 0.24.0 — (unreleased)
1
+ ## 0.25.0 — (unreleased)
2
+
3
+ No changes yet.
4
+
5
+
6
+
7
+ ## 0.24.0 — February 1st, 2017
2
8
 
3
9
  ### Configurable Consumer Prefixes
4
10
 
data/Gemfile CHANGED
@@ -23,7 +23,7 @@ group :development, :test do
23
23
  gem "honeybadger"
24
24
  gem "coveralls", "~> 0.8.15", require: false
25
25
  gem "newrelic_rpm"
26
- gem "airbrake", "~> 5.0"
26
+ gem "airbrake", "~> 7.0"
27
27
  gem "opbeat", "~> 3.0.9"
28
28
  end
29
29
 
data/README.md CHANGED
@@ -80,8 +80,8 @@ message[:id] # => "02ABCXYZ"
80
80
  To subscribe to a topic, pass a routing key to `consume` in the class
81
81
  definition. To bind to multiple routing keys, simply pass extra routing keys
82
82
  in as additional arguments. Refer to the [RabbitMQ docs on topic exchanges
83
- ][topic-docs] for more information about how to use routing keys. Here's an
84
- example consumer:
83
+ ](http://www.rabbitmq.com/tutorials/tutorial-five-ruby.html) for more information
84
+ about how to use routing keys. Here's an example consumer:
85
85
 
86
86
  ```ruby
87
87
  class FailedPaymentConsumer
@@ -120,7 +120,9 @@ class FailedPaymentConsumer
120
120
  end
121
121
  ```
122
122
 
123
- Custom queue arguments can be found on [this page](https://www.rabbitmq.com/extensions.html).
123
+ This sets the `x-max-length` header. For more details, see the [RabbitMQ
124
+ documentation on Queue Length Limit](https://www.rabbitmq.com/maxlength.html). To find out more
125
+ about custom queue arguments, consult the [RabbitMQ documentation on AMQP Protocol Extensions](https://www.rabbitmq.com/extensions.html).
124
126
 
125
127
  Consumers can write to Hutch's log by calling the logger method. The logger method returns
126
128
  a [Logger object](http://ruby-doc.org/stdlib-2.1.2/libdoc/logger/rdoc/Logger.html).
@@ -300,8 +302,8 @@ AMQP.connect(host: config[:host]) do |connection|
300
302
  end
301
303
  ```
302
304
 
303
- If using publisher confirms with amqp gem, see [this issue][pc-issue]
304
- and [this gist][pc-gist] for more info.
305
+ If using publisher confirms with amqp gem, see [this issue](https://github.com/ruby-amqp/amqp/issues/92)
306
+ and [this gist](https://gist.github.com/3042381) for more info.
305
307
 
306
308
  ## Configuration
307
309
 
@@ -384,241 +386,188 @@ Generate with
384
386
  </tr>
385
387
  </thead>
386
388
  <tbody>
387
-
388
389
  <tr>
389
390
  <td><tt>mq_host</tt></td>
390
391
  <td>127.0.0.1</td>
391
392
  <td>String</td>
392
393
  <td><tt>HUTCH_MQ_HOST</tt></td>
393
- <td><p>RabbitMQ hostname</p>
394
- </td>
394
+ <td><p>RabbitMQ hostname</p></td>
395
395
  </tr>
396
-
397
396
  <tr>
398
397
  <td><tt>mq_exchange</tt></td>
399
398
  <td>hutch</td>
400
399
  <td>String</td>
401
400
  <td><tt>HUTCH_MQ_EXCHANGE</tt></td>
402
- <td><p>RabbitMQ Exchange to use for publishing</p>
403
- </td>
401
+ <td><p>RabbitMQ Exchange to use for publishing</p></td>
404
402
  </tr>
405
-
406
403
  <tr>
407
404
  <td><tt>mq_vhost</tt></td>
408
405
  <td>/</td>
409
406
  <td>String</td>
410
407
  <td><tt>HUTCH_MQ_VHOST</tt></td>
411
- <td><p>RabbitMQ vhost to use</p>
412
- </td>
408
+ <td><p>RabbitMQ vhost to use</p></td>
413
409
  </tr>
414
-
415
410
  <tr>
416
411
  <td><tt>mq_username</tt></td>
417
412
  <td>guest</td>
418
413
  <td>String</td>
419
414
  <td><tt>HUTCH_MQ_USERNAME</tt></td>
420
- <td><p>RabbitMQ username to use.</p>
421
- </td>
415
+ <td><p>RabbitMQ username to use.</p></td>
422
416
  </tr>
423
-
424
417
  <tr>
425
418
  <td><tt>mq_password</tt></td>
426
419
  <td>guest</td>
427
420
  <td>String</td>
428
421
  <td><tt>HUTCH_MQ_PASSWORD</tt></td>
429
- <td><p>RabbitMQ password</p>
430
- </td>
422
+ <td><p>RabbitMQ password</p></td>
431
423
  </tr>
432
-
433
424
  <tr>
434
425
  <td><tt>uri</tt></td>
435
426
  <td>nil</td>
436
427
  <td>String</td>
437
428
  <td><tt>HUTCH_URI</tt></td>
438
- <td><p>RabbitMQ URI (takes precedence over MQ username, password, host, port and vhost settings)</p>
439
- </td>
429
+ <td><p>RabbitMQ URI (takes precedence over MQ username, password, host, port and vhost settings)</p></td>
440
430
  </tr>
441
-
442
431
  <tr>
443
432
  <td><tt>mq_api_host</tt></td>
444
433
  <td>127.0.0.1</td>
445
434
  <td>String</td>
446
435
  <td><tt>HUTCH_MQ_API_HOST</tt></td>
447
- <td><p>RabbitMQ HTTP API hostname</p>
448
- </td>
436
+ <td><p>RabbitMQ HTTP API hostname</p></td>
449
437
  </tr>
450
-
451
438
  <tr>
452
439
  <td><tt>mq_port</tt></td>
453
440
  <td>5672</td>
454
441
  <td>Number</td>
455
442
  <td><tt>HUTCH_MQ_PORT</tt></td>
456
- <td><p>RabbitMQ port</p>
457
- </td>
443
+ <td><p>RabbitMQ port</p></td>
458
444
  </tr>
459
-
460
445
  <tr>
461
446
  <td><tt>mq_api_port</tt></td>
462
447
  <td>15672</td>
463
448
  <td>Number</td>
464
449
  <td><tt>HUTCH_MQ_API_PORT</tt></td>
465
- <td><p>RabbitMQ HTTP API port</p>
466
- </td>
450
+ <td><p>RabbitMQ HTTP API port</p></td>
467
451
  </tr>
468
-
469
452
  <tr>
470
453
  <td><tt>heartbeat</tt></td>
471
454
  <td>30</td>
472
455
  <td>Number</td>
473
456
  <td><tt>HUTCH_HEARTBEAT</tt></td>
474
- <td><p><a href="http://rabbitmq.com/heartbeats.html">RabbitMQ heartbeat timeout</a></p>
475
- </td>
457
+ <td><p><a href="http://rabbitmq.com/heartbeats.html">RabbitMQ heartbeat timeout</a></p></td>
476
458
  </tr>
477
-
478
459
  <tr>
479
460
  <td><tt>channel_prefetch</tt></td>
480
461
  <td>0</td>
481
462
  <td>Number</td>
482
463
  <td><tt>HUTCH_CHANNEL_PREFETCH</tt></td>
483
- <td><p>The <tt>basic.qos</tt> prefetch value to use.</p>
484
- </td>
464
+ <td><p>The <tt>basic.qos</tt> prefetch value to use.</p></td>
485
465
  </tr>
486
-
487
466
  <tr>
488
467
  <td><tt>connection_timeout</tt></td>
489
468
  <td>11</td>
490
469
  <td>Number</td>
491
470
  <td><tt>HUTCH_CONNECTION_TIMEOUT</tt></td>
492
- <td><p>Bunny's socket open timeout</p>
493
- </td>
471
+ <td><p>Bunny's socket open timeout</p></td>
494
472
  </tr>
495
-
496
473
  <tr>
497
474
  <td><tt>read_timeout</tt></td>
498
475
  <td>11</td>
499
476
  <td>Number</td>
500
477
  <td><tt>HUTCH_READ_TIMEOUT</tt></td>
501
- <td><p>Bunny's socket read timeout</p>
502
- </td>
478
+ <td><p>Bunny's socket read timeout</p></td>
503
479
  </tr>
504
-
505
480
  <tr>
506
481
  <td><tt>write_timeout</tt></td>
507
482
  <td>11</td>
508
483
  <td>Number</td>
509
484
  <td><tt>HUTCH_WRITE_TIMEOUT</tt></td>
510
- <td><p>Bunny's socket write timeout</p>
511
- </td>
485
+ <td><p>Bunny's socket write timeout</p></td>
512
486
  </tr>
513
-
514
487
  <tr>
515
488
  <td><tt>graceful_exit_timeout</tt></td>
516
489
  <td>11</td>
517
490
  <td>Number</td>
518
491
  <td><tt>HUTCH_GRACEFUL_EXIT_TIMEOUT</tt></td>
519
- <td><p>FIXME: DOCUMENT THIS</p>
520
- </td>
492
+ <td><p>FIXME: DOCUMENT THIS</p></td>
521
493
  </tr>
522
-
523
494
  <tr>
524
495
  <td><tt>consumer_pool_size</tt></td>
525
496
  <td>1</td>
526
497
  <td>Number</td>
527
498
  <td><tt>HUTCH_CONSUMER_POOL_SIZE</tt></td>
528
- <td><p>Bunny consumer work pool size</p>
529
- </td>
499
+ <td><p>Bunny consumer work pool size</p></td>
530
500
  </tr>
531
-
532
501
  <tr>
533
502
  <td><tt>mq_tls</tt></td>
534
503
  <td>false</td>
535
504
  <td>Boolean</td>
536
505
  <td><tt>HUTCH_MQ_TLS</tt></td>
537
- <td><p>Should TLS be used?</p>
538
- </td>
506
+ <td><p>Should TLS be used?</p></td>
539
507
  </tr>
540
-
541
508
  <tr>
542
509
  <td><tt>mq_verify_peer</tt></td>
543
510
  <td>true</td>
544
511
  <td>Boolean</td>
545
512
  <td><tt>HUTCH_MQ_VERIFY_PEER</tt></td>
546
- <td><p>Should SSL certificate be verified?</p>
547
- </td>
513
+ <td><p>Should SSL certificate be verified?</p></td>
548
514
  </tr>
549
-
550
515
  <tr>
551
516
  <td><tt>mq_api_ssl</tt></td>
552
517
  <td>false</td>
553
518
  <td>Boolean</td>
554
519
  <td><tt>HUTCH_MQ_API_SSL</tt></td>
555
- <td><p>Should SSL be used for the RabbitMQ API?</p>
556
- </td>
520
+ <td><p>Should SSL be used for the RabbitMQ API?</p></td>
557
521
  </tr>
558
-
559
522
  <tr>
560
523
  <td><tt>autoload_rails</tt></td>
561
524
  <td>true</td>
562
525
  <td>Boolean</td>
563
526
  <td><tt>HUTCH_AUTOLOAD_RAILS</tt></td>
564
- <td><p>Should the current Rails app directory be required?</p>
565
- </td>
527
+ <td><p>Should the current Rails app directory be required?</p></td>
566
528
  </tr>
567
-
568
529
  <tr>
569
530
  <td><tt>daemonise</tt></td>
570
531
  <td>false</td>
571
532
  <td>Boolean</td>
572
533
  <td><tt>HUTCH_DAEMONISE</tt></td>
573
- <td><p>Should the Hutch runner process daemonise?</p>
574
- </td>
534
+ <td><p>Should the Hutch runner process daemonise?</p></td>
575
535
  </tr>
576
-
577
536
  <tr>
578
537
  <td><tt>publisher_confirms</tt></td>
579
538
  <td>false</td>
580
539
  <td>Boolean</td>
581
540
  <td><tt>HUTCH_PUBLISHER_CONFIRMS</tt></td>
582
- <td><p>Should RabbitMQ publisher confirms be enabled?</p>
583
- </td>
541
+ <td><p>Should RabbitMQ publisher confirms be enabled?</p></td>
584
542
  </tr>
585
-
586
543
  <tr>
587
544
  <td><tt>force_publisher_confirms</tt></td>
588
545
  <td>false</td>
589
546
  <td>Boolean</td>
590
547
  <td><tt>HUTCH_FORCE_PUBLISHER_CONFIRMS</tt></td>
591
- <td><p>Enables publisher confirms, forces Hutch::Broker#wait_for_confirms for</p>
592
- </td>
548
+ <td><p>Enables publisher confirms, forces Hutch::Broker#wait_for_confirms for</p></td>
593
549
  </tr>
594
-
595
550
  <tr>
596
551
  <td><tt>enable_http_api_use</tt></td>
597
552
  <td>true</td>
598
553
  <td>Boolean</td>
599
554
  <td><tt>HUTCH_ENABLE_HTTP_API_USE</tt></td>
600
- <td><p>Should the RabbitMQ HTTP API be used?</p>
601
- </td>
555
+ <td><p>Should the RabbitMQ HTTP API be used?</p></td>
602
556
  </tr>
603
-
604
557
  <tr>
605
558
  <td><tt>consumer_pool_abort_on_exception</tt></td>
606
559
  <td>false</td>
607
560
  <td>Boolean</td>
608
561
  <td><tt>HUTCH_CONSUMER_POOL_ABORT_ON_EXCEPTION</tt></td>
609
- <td><p>Should Bunny's consumer work pool threads abort on exception.</p>
610
- </td>
562
+ <td><p>Should Bunny's consumer work pool threads abort on exception.</p></td>
611
563
  </tr>
612
-
613
564
  <tr>
614
565
  <td><tt>consumer_tag_prefix</tt></td>
615
566
  <td>hutch</td>
616
567
  <td>String</td>
617
568
  <td><tt>HUTCH_CONSUMER_TAG_PREFIX</tt></td>
618
- <td><p>Prefix displayed on the consumers tags.</p>
619
- </td>
569
+ <td><p>Prefix displayed on the consumers tags.</p></td>
620
570
  </tr>
621
-
622
571
  </tbody>
623
572
  </table>
624
573
 
data/hutch.gemspec CHANGED
@@ -3,10 +3,10 @@ require File.expand_path('../lib/hutch/version', __FILE__)
3
3
  Gem::Specification.new do |gem|
4
4
  if defined?(JRUBY_VERSION)
5
5
  gem.platform = 'java'
6
- gem.add_runtime_dependency 'march_hare', '>= 2.22.0'
6
+ gem.add_runtime_dependency 'march_hare', '>= 3.0.0'
7
7
  else
8
8
  gem.platform = Gem::Platform::RUBY
9
- gem.add_runtime_dependency 'bunny', '>= 2.6.3'
9
+ gem.add_runtime_dependency 'bunny', '~> 2.9.0'
10
10
  end
11
11
  gem.add_runtime_dependency 'carrot-top', '~> 0.0.7'
12
12
  gem.add_runtime_dependency 'multi_json', '~> 1.12'
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
17
17
  gem.description = 'Hutch is a Ruby library for enabling asynchronous ' \
18
18
  'inter-service communication using RabbitMQ.'
19
19
  gem.version = Hutch::VERSION.dup
20
- gem.required_ruby_version = '>= 2.0'
20
+ gem.required_ruby_version = '>= 2.2'
21
21
  gem.authors = ['Harry Marr']
22
22
  gem.email = ['developers@gocardless.com']
23
23
  gem.homepage = 'https://github.com/gocardless/hutch'
data/lib/hutch/broker.rb CHANGED
@@ -11,6 +11,24 @@ module Hutch
11
11
 
12
12
  attr_accessor :connection, :channel, :exchange, :api_client
13
13
 
14
+
15
+ DEFAULT_AMQP_PORT =
16
+ case RUBY_ENGINE
17
+ when "jruby" then
18
+ com.rabbitmq.client.ConnectionFactory::DEFAULT_AMQP_PORT
19
+ when "ruby" then
20
+ AMQ::Protocol::DEFAULT_PORT
21
+ end
22
+
23
+ DEFAULT_AMQPS_PORT =
24
+ case RUBY_ENGINE
25
+ when "jruby" then
26
+ com.rabbitmq.client.ConnectionFactory::DEFAULT_AMQP_OVER_SSL_PORT
27
+ when "ruby" then
28
+ AMQ::Protocol::TLS_PORT
29
+ end
30
+
31
+
14
32
  # @param config [nil,Hash] Configuration override
15
33
  def initialize(config = nil)
16
34
  @config = config || Hutch::Config
@@ -292,13 +310,18 @@ module Hutch
292
310
 
293
311
  u = URI.parse(@config[:uri])
294
312
 
313
+ @config[:mq_tls] = u.scheme == 'amqps'
295
314
  @config[:mq_host] = u.host
296
- @config[:mq_port] = u.port
315
+ @config[:mq_port] = u.port || default_mq_port
297
316
  @config[:mq_vhost] = u.path.sub(/^\//, "")
298
317
  @config[:mq_username] = u.user
299
318
  @config[:mq_password] = u.password
300
319
  end
301
320
 
321
+ def default_mq_port
322
+ @config[:mq_tls] ? DEFAULT_AMQPS_PORT : DEFAULT_AMQP_PORT
323
+ end
324
+
302
325
  def sanitized_uri
303
326
  p = connection_params
304
327
  scheme = p[:tls] ? "amqps" : "amqp"
data/lib/hutch/cli.rb CHANGED
@@ -72,8 +72,8 @@ module Hutch
72
72
  end
73
73
  rails_path = File.expand_path(File.join(path, 'config/environment.rb'))
74
74
  if is_rails_app && File.exist?(rails_path)
75
- logger.info "found rails project (#{path}), booting app"
76
75
  ENV['RACK_ENV'] ||= ENV['RAILS_ENV'] || 'development'
76
+ logger.info "found rails project (#{path}), booting app in #{ENV['RACK_ENV']} environment"
77
77
  require rails_path
78
78
  ::Rails.application.eager_load!
79
79
  return true
@@ -90,6 +90,9 @@ module Hutch
90
90
  @worker.run
91
91
  :success
92
92
  rescue ConnectionError, AuthenticationError, WorkerSetupError => ex
93
+ Hutch::Config[:error_handlers].each do |backend|
94
+ backend.handle_setup_exception(ex)
95
+ end
93
96
  logger.fatal ex.message
94
97
  :error
95
98
  end
@@ -189,6 +192,10 @@ module Hutch
189
192
  Hutch::Config.pidfile = pidfile
190
193
  end
191
194
 
195
+ opts.on('--only-group GROUP', 'Load only consumers in this group') do |group|
196
+ Hutch::Config.group = group
197
+ end
198
+
192
199
  opts.on('--version', 'Print the version and exit') do
193
200
  puts "hutch v#{VERSION}"
194
201
  exit 0
data/lib/hutch/config.rb CHANGED
@@ -136,6 +136,8 @@ module Hutch
136
136
  # Prefix displayed on the consumers tags.
137
137
  string_setting :consumer_tag_prefix, 'hutch'
138
138
 
139
+ string_setting :group, ''
140
+
139
141
  # Set of all setting keys
140
142
  ALL_KEYS = @boolean_keys + @number_keys + @string_keys
141
143
 
@@ -167,6 +169,7 @@ module Hutch
167
169
  # that will fall back to "nack unconditionally"
168
170
  error_acknowledgements: [],
169
171
  setup_procs: [],
172
+ consumer_groups: {},
170
173
  tracer: Hutch::Tracers::NullTracer,
171
174
  namespace: nil,
172
175
  pidfile: nil,
@@ -1,10 +1,10 @@
1
1
  require 'hutch/logging'
2
2
  require 'airbrake'
3
+ require 'hutch/error_handlers/base'
3
4
 
4
5
  module Hutch
5
6
  module ErrorHandlers
6
- class Airbrake
7
- include Logging
7
+ class Airbrake < Base
8
8
 
9
9
  def handle(properties, payload, consumer, ex)
10
10
  message_id = properties.message_id
@@ -31,6 +31,24 @@ module Hutch
31
31
  })
32
32
  end
33
33
  end
34
+
35
+ def handle_setup_exception(ex)
36
+ logger.error "Logging setup exception to Airbrake"
37
+ logger.error "#{ex.class} - #{ex.message}"
38
+
39
+ if ::Airbrake.respond_to?(:notify_or_ignore)
40
+ ::Airbrake.notify_or_ignore(ex, {
41
+ error_class: ex.class.name,
42
+ error_message: "#{ ex.class.name }: #{ ex.message }",
43
+ backtrace: ex.backtrace,
44
+ cgi_data: ENV.to_hash,
45
+ })
46
+ else
47
+ ::Airbrake.notify(ex, {
48
+ cgi_data: ENV.to_hash,
49
+ })
50
+ end
51
+ end
34
52
  end
35
53
  end
36
54
  end
@@ -0,0 +1,15 @@
1
+ module Hutch
2
+ module ErrorHandlers
3
+ class Base
4
+ include Logging
5
+
6
+ def handle(properties, payload, consumer, ex)
7
+ raise NotImplementedError.new
8
+ end
9
+
10
+ def handle_setup_exception(ex)
11
+ raise NotImplementedError.new
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,28 +1,42 @@
1
1
  require 'hutch/logging'
2
2
  require 'honeybadger'
3
+ require 'hutch/error_handlers/base'
3
4
 
4
5
  module Hutch
5
6
  module ErrorHandlers
6
- class Honeybadger
7
- include Logging
7
+ # Error handler for the Honeybadger.io service
8
+ class Honeybadger < Base
8
9
 
9
10
  def handle(properties, payload, consumer, ex)
10
11
  message_id = properties.message_id
11
12
  prefix = "message(#{message_id || '-'}):"
12
13
  logger.error "#{prefix} Logging event to Honeybadger"
13
14
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
14
- ::Honeybadger.notify_or_ignore(
15
- :error_class => ex.class.name,
16
- :error_message => "#{ ex.class.name }: #{ ex.message }",
17
- :backtrace => ex.backtrace,
18
- :context => {
19
- :message_id => message_id,
20
- :consumer => consumer
21
- },
22
- :parameters => {
23
- :payload => payload
24
- }
25
- )
15
+ notify_honeybadger(error_class: ex.class.name,
16
+ error_message: "#{ex.class.name}: #{ex.message}",
17
+ backtrace: ex.backtrace,
18
+ context: { message_id: message_id,
19
+ consumer: consumer },
20
+ parameters: { payload: payload })
21
+ end
22
+
23
+ def handle_setup_exception(ex)
24
+ logger.error "Logging setup exception to Honeybadger"
25
+ logger.error "#{ex.class} - #{ex.message}"
26
+ notify_honeybadger(error_class: ex.class.name,
27
+ error_message: "#{ex.class.name}: #{ex.message}",
28
+ backtrace: ex.backtrace)
29
+ end
30
+
31
+ # Wrap API to support 3.0.0+
32
+ #
33
+ # @see https://github.com/honeybadger-io/honeybadger-ruby/blob/master/CHANGELOG.md#300---2017-02-06
34
+ def notify_honeybadger(message)
35
+ if ::Honeybadger.respond_to?(:notify_or_ignore)
36
+ ::Honeybadger.notify_or_ignore(message)
37
+ else
38
+ ::Honeybadger.notify(message)
39
+ end
26
40
  end
27
41
  end
28
42
  end
@@ -1,9 +1,9 @@
1
1
  require 'hutch/logging'
2
+ require 'hutch/error_handlers/base'
2
3
 
3
4
  module Hutch
4
5
  module ErrorHandlers
5
- class Logger
6
- include Logging
6
+ class Logger < ErrorHandlers::Base
7
7
 
8
8
  def handle(properties, payload, consumer, ex)
9
9
  message_id = properties.message_id
@@ -12,6 +12,11 @@ module Hutch
12
12
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
13
13
  logger.error (['backtrace:'] + ex.backtrace).join("\n")
14
14
  end
15
+
16
+ def handle_setup_exception(ex)
17
+ logger.error "#{ex.class} - #{ex.message}"
18
+ logger.error (['backtrace:'] + ex.backtrace).join("\n")
19
+ end
15
20
  end
16
21
  end
17
22
  end
@@ -1,10 +1,10 @@
1
1
  require 'hutch/logging'
2
2
  require 'opbeat'
3
+ require 'hutch/error_handlers/base'
3
4
 
4
5
  module Hutch
5
6
  module ErrorHandlers
6
- class Opbeat
7
- include Logging
7
+ class Opbeat < Base
8
8
 
9
9
  def initialize
10
10
  unless ::Opbeat.respond_to?(:report)
@@ -19,6 +19,12 @@ module Hutch
19
19
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
20
20
  ::Opbeat.report(ex, extra: { payload: payload })
21
21
  end
22
+
23
+ def handle_setup_exception(ex)
24
+ logger.error "Logging setup exception to Opbeat"
25
+ logger.error "#{ex.class} - #{ex.message}"
26
+ ::Opbeat.report(ex)
27
+ end
22
28
  end
23
29
  end
24
30
  end
@@ -1,10 +1,10 @@
1
1
  require 'hutch/logging'
2
2
  require 'raven'
3
+ require 'hutch/error_handlers/base'
3
4
 
4
5
  module Hutch
5
6
  module ErrorHandlers
6
- class Sentry
7
- include Logging
7
+ class Sentry < Base
8
8
 
9
9
  def initialize
10
10
  unless Raven.respond_to?(:capture_exception)
@@ -19,6 +19,13 @@ module Hutch
19
19
  logger.error "#{prefix} #{ex.class} - #{ex.message}"
20
20
  Raven.capture_exception(ex, extra: { payload: payload })
21
21
  end
22
+
23
+ def handle_setup_exception(ex)
24
+ logger.error "Logging setup exception to Sentry"
25
+ logger.error "#{ex.class} - #{ex.message}"
26
+ Raven.capture_exception(ex)
27
+ end
28
+
22
29
  end
23
30
  end
24
31
  end
data/lib/hutch/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Hutch
2
- VERSION = '0.24.0'.freeze
2
+ VERSION = '0.25.0'.freeze
3
3
  end
4
4
 
data/lib/hutch/waiter.rb CHANGED
@@ -50,7 +50,7 @@ module Hutch
50
50
  end
51
51
  end
52
52
 
53
- # @raises ContinueProcessingSignals
53
+ # @raise ContinueProcessingSignals
54
54
  def handle_user_signal(sig)
55
55
  case sig
56
56
  when 'USR2' then log_thread_backtraces
data/lib/hutch/worker.rb CHANGED
@@ -36,12 +36,17 @@ module Hutch
36
36
  # Set up the queues for each of the worker's consumers.
37
37
  def setup_queues
38
38
  logger.info 'setting up queues'
39
- @consumers.each { |consumer| setup_queue(consumer) }
39
+ vetted = @consumers.reject { |c| group_configured? && group_restricted?(c) }
40
+ vetted.each do |c|
41
+ setup_queue(c)
42
+ end
40
43
  end
41
44
 
42
45
  # Bind a consumer's routing keys to its queue, and set up a subscription to
43
46
  # receive messages sent to the queue.
44
47
  def setup_queue(consumer)
48
+ logger.info "setting up queue: #{consumer.get_queue_name}"
49
+
45
50
  queue = @broker.queue(consumer.get_queue_name, consumer.get_arguments)
46
51
  @broker.bind_queue(queue, consumer.routing_keys)
47
52
 
@@ -103,6 +108,30 @@ module Hutch
103
108
 
104
109
  private
105
110
 
111
+ def group_configured?
112
+ if group.present? && consumer_groups.blank?
113
+ logger.info 'Consumer groups are blank'
114
+ end
115
+ group.present?
116
+ end
117
+
118
+ def group_restricted?(consumer)
119
+ consumers_to_load = consumer_groups[group]
120
+ if consumers_to_load
121
+ !allowed_consumers.include?(consumer.name)
122
+ else
123
+ true
124
+ end
125
+ end
126
+
127
+ def group
128
+ Hutch::Config[:group]
129
+ end
130
+
131
+ def consumer_groups
132
+ Hutch::Config[:consumer_groups]
133
+ end
134
+
106
135
  attr_accessor :setup_procs
107
136
 
108
137
  def unique_consumer_tag
@@ -92,6 +92,40 @@ describe Hutch::Broker do
92
92
 
93
93
  connection.close
94
94
  end
95
+
96
+ context 'when configured with a URI' do
97
+ context 'which specifies the port' do
98
+ before { config[:uri] = 'amqp://guest:guest@127.0.0.1:5672/' }
99
+
100
+ it 'successfully connects' do
101
+ c = broker.open_connection
102
+ expect(c).to be_open
103
+ c.close
104
+ end
105
+ end
106
+
107
+ context 'which does not specify port and uses the amqp scheme' do
108
+ before { config[:uri] = 'amqp://guest:guest@127.0.0.1/' }
109
+
110
+ it 'successfully connects' do
111
+ c = broker.open_connection
112
+ expect(c).to be_open
113
+ c.close
114
+ end
115
+ end
116
+
117
+ context 'which specifies the amqps scheme' do
118
+ before { config[:uri] = 'amqps://guest:guest@127.0.0.1/' }
119
+
120
+ it 'utilises TLS' do
121
+ expect(Hutch::Adapter).to receive(:new).with(
122
+ hash_including(tls: true, port: 5671)
123
+ ).and_return(instance_double('Hutch::Adapter', start: nil))
124
+
125
+ broker.open_connection
126
+ end
127
+ end
128
+ end
95
129
  end
96
130
 
97
131
  describe '#open_connection!' do
@@ -4,6 +4,19 @@ require 'tempfile'
4
4
  describe Hutch::CLI do
5
5
  let(:cli) { Hutch::CLI.new }
6
6
 
7
+ describe "#start_work_loop" do
8
+ context "connection error during setup" do
9
+ let(:error) { Hutch::ConnectionError.new }
10
+ it "gets reported using error handlers" do
11
+ allow(Hutch).to receive(:connect).and_raise(error)
12
+ Hutch::Config[:error_handlers].each do |backend|
13
+ expect(backend).to receive(:handle_setup_exception).with(error)
14
+ end
15
+ cli.start_work_loop
16
+ end
17
+ end
18
+ end
19
+
7
20
  describe "#parse_options" do
8
21
  context "--config" do
9
22
  context "when the config file does not exist" do
@@ -27,4 +27,23 @@ describe Hutch::ErrorHandlers::Airbrake do
27
27
  error_handler.handle(properties, payload, consumer, ex)
28
28
  end
29
29
  end
30
+
31
+ describe '#handle_setup_exception' do
32
+ let(:error) do
33
+ begin
34
+ raise "Stuff went wrong"
35
+ rescue RuntimeError => err
36
+ err
37
+ end
38
+ end
39
+
40
+ it "logs the error to Airbrake" do
41
+ ex = error
42
+ message = {
43
+ cgi_data: ENV.to_hash,
44
+ }
45
+ expect(::Airbrake).to receive(:notify).with(ex, message)
46
+ error_handler.handle_setup_exception(ex)
47
+ end
48
+ end
30
49
  end
@@ -30,8 +30,29 @@ describe Hutch::ErrorHandlers::Honeybadger do
30
30
  :payload => payload
31
31
  }
32
32
  }
33
- expect(::Honeybadger).to receive(:notify_or_ignore).with(message)
33
+ expect(error_handler).to receive(:notify_honeybadger).with(message)
34
34
  error_handler.handle(properties, payload, consumer, ex)
35
35
  end
36
36
  end
37
+
38
+ describe '#handle_setup_exception' do
39
+ let(:error) do
40
+ begin
41
+ raise "Stuff went wrong during setup"
42
+ rescue RuntimeError => err
43
+ err
44
+ end
45
+ end
46
+
47
+ it "logs the error to Honeybadger" do
48
+ ex = error
49
+ message = {
50
+ :error_class => ex.class.name,
51
+ :error_message => "#{ ex.class.name }: #{ ex.message }",
52
+ :backtrace => ex.backtrace,
53
+ }
54
+ expect(error_handler).to receive(:notify_honeybadger).with(message)
55
+ error_handler.handle_setup_exception(ex)
56
+ end
57
+ end
37
58
  end
@@ -14,4 +14,15 @@ describe Hutch::ErrorHandlers::Logger do
14
14
  error_handler.handle(properties, payload, double, error)
15
15
  end
16
16
  end
17
+
18
+ describe '#handle_setup_exception' do
19
+ let(:error) { double(message: "Stuff went wrong during setup",
20
+ class: "RuntimeError",
21
+ backtrace: ["line 1", "line 2"]) }
22
+
23
+ it "logs two separate lines" do
24
+ expect(Hutch::Logging.logger).to receive(:error).exactly(2).times
25
+ error_handler.handle_setup_exception(error)
26
+ end
27
+ end
17
28
  end
@@ -19,4 +19,19 @@ describe Hutch::ErrorHandlers::Opbeat do
19
19
  error_handler.handle(properties, payload, double, error)
20
20
  end
21
21
  end
22
+
23
+ describe '#handle_setup_exception' do
24
+ let(:error) do
25
+ begin
26
+ raise "Stuff went wrong during setup"
27
+ rescue RuntimeError => err
28
+ err
29
+ end
30
+ end
31
+
32
+ it "logs the error to Opbeat" do
33
+ expect(Opbeat).to receive(:report).with(error)
34
+ error_handler.handle_setup_exception(error)
35
+ end
36
+ end
22
37
  end
@@ -19,4 +19,19 @@ describe Hutch::ErrorHandlers::Sentry do
19
19
  error_handler.handle(properties, payload, double, error)
20
20
  end
21
21
  end
22
+
23
+ describe '#handle_setup_exception' do
24
+ let(:error) do
25
+ begin
26
+ raise "Stuff went wrong during setup"
27
+ rescue RuntimeError => err
28
+ err
29
+ end
30
+ end
31
+
32
+ it "logs the error to Sentry" do
33
+ expect(Raven).to receive(:capture_exception).with(error)
34
+ error_handler.handle_setup_exception(error)
35
+ end
36
+ end
22
37
  end
@@ -97,7 +97,7 @@ describe Hutch::Worker do
97
97
  it 'requeues the message' do
98
98
  allow(consumer_instance).to receive(:process).and_raise('failed')
99
99
  requeuer = double
100
- allow(requeuer).to receive(:handle).ordered { |delivery_info, properties, broker, e|
100
+ allow(requeuer).to receive(:handle) { |delivery_info, properties, broker, e|
101
101
  broker.requeue delivery_info.delivery_tag
102
102
  true
103
103
  }
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hutch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-01 00:00:00.000000000 Z
11
+ date: 2018-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.6.3
19
+ version: 2.9.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.6.3
26
+ version: 2.9.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: carrot-top
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -107,6 +107,7 @@ files:
107
107
  - lib/hutch/consumer.rb
108
108
  - lib/hutch/error_handlers.rb
109
109
  - lib/hutch/error_handlers/airbrake.rb
110
+ - lib/hutch/error_handlers/base.rb
110
111
  - lib/hutch/error_handlers/honeybadger.rb
111
112
  - lib/hutch/error_handlers/logger.rb
112
113
  - lib/hutch/error_handlers/opbeat.rb
@@ -164,7 +165,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
165
  requirements:
165
166
  - - ">="
166
167
  - !ruby/object:Gem::Version
167
- version: '2.0'
168
+ version: '2.2'
168
169
  required_rubygems_version: !ruby/object:Gem::Requirement
169
170
  requirements:
170
171
  - - ">="
@@ -172,7 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
173
  version: '0'
173
174
  requirements: []
174
175
  rubyforge_project:
175
- rubygems_version: 2.5.1
176
+ rubygems_version: 2.6.11
176
177
  signing_key:
177
178
  specification_version: 4
178
179
  summary: Easy inter-service communication using RabbitMQ.