hutch 0.21.0-java → 0.25.0-java

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.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +11 -12
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +118 -1
  7. data/Gemfile +15 -4
  8. data/Guardfile +13 -4
  9. data/README.md +274 -24
  10. data/Rakefile +8 -1
  11. data/hutch.gemspec +6 -7
  12. data/lib/hutch.rb +11 -8
  13. data/lib/hutch/adapters/march_hare.rb +1 -1
  14. data/lib/hutch/broker.rb +113 -110
  15. data/lib/hutch/cli.rb +42 -11
  16. data/lib/hutch/config.rb +209 -59
  17. data/lib/hutch/error_handlers.rb +1 -0
  18. data/lib/hutch/error_handlers/airbrake.rb +44 -16
  19. data/lib/hutch/error_handlers/base.rb +15 -0
  20. data/lib/hutch/error_handlers/honeybadger.rb +33 -18
  21. data/lib/hutch/error_handlers/logger.rb +12 -6
  22. data/lib/hutch/error_handlers/opbeat.rb +30 -0
  23. data/lib/hutch/error_handlers/sentry.rb +14 -6
  24. data/lib/hutch/logging.rb +5 -5
  25. data/lib/hutch/publisher.rb +75 -0
  26. data/lib/hutch/tracers.rb +1 -0
  27. data/lib/hutch/tracers/opbeat.rb +37 -0
  28. data/lib/hutch/version.rb +1 -1
  29. data/lib/hutch/waiter.rb +104 -0
  30. data/lib/hutch/worker.rb +50 -66
  31. data/lib/yard-settings/handler.rb +38 -0
  32. data/lib/yard-settings/yard-settings.rb +2 -0
  33. data/spec/hutch/broker_spec.rb +162 -77
  34. data/spec/hutch/cli_spec.rb +16 -3
  35. data/spec/hutch/config_spec.rb +83 -22
  36. data/spec/hutch/error_handlers/airbrake_spec.rb +25 -10
  37. data/spec/hutch/error_handlers/honeybadger_spec.rb +24 -2
  38. data/spec/hutch/error_handlers/logger_spec.rb +14 -1
  39. data/spec/hutch/error_handlers/opbeat_spec.rb +37 -0
  40. data/spec/hutch/error_handlers/sentry_spec.rb +18 -1
  41. data/spec/hutch/logger_spec.rb +12 -6
  42. data/spec/hutch/waiter_spec.rb +51 -0
  43. data/spec/hutch/worker_spec.rb +33 -4
  44. data/spec/spec_helper.rb +7 -5
  45. data/spec/tracers/opbeat_spec.rb +44 -0
  46. data/templates/default/class/html/settings.erb +0 -0
  47. data/templates/default/class/setup.rb +4 -0
  48. data/templates/default/fulldoc/html/css/hutch.css +13 -0
  49. data/templates/default/layout/html/setup.rb +7 -0
  50. data/templates/default/method_details/html/settings.erb +5 -0
  51. data/templates/default/method_details/setup.rb +4 -0
  52. data/templates/default/method_details/text/settings.erb +0 -0
  53. data/templates/default/module/html/settings.erb +40 -0
  54. data/templates/default/module/setup.rb +4 -0
  55. metadata +41 -38
@@ -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
@@ -13,7 +26,7 @@ describe Hutch::CLI do
13
26
  it "bails" do
14
27
  expect {
15
28
  cli.parse_options(["--config=#{file}"])
16
- }.to raise_error SystemExit
29
+ }.to raise_error SystemExit, "Config file '/path/to/nonexistant/file' not found"
17
30
  end
18
31
  end
19
32
 
@@ -37,7 +50,7 @@ describe Hutch::CLI do
37
50
  it "bails" do
38
51
  expect {
39
52
  cli.parse_options(["--mq-tls-key=#{file}"])
40
- }.to raise_error SystemExit
53
+ }.to raise_error SystemExit, "Private key file '/path/to/nonexistant/file' not found"
41
54
  end
42
55
  end
43
56
 
@@ -61,7 +74,7 @@ describe Hutch::CLI do
61
74
  it "bails" do
62
75
  expect {
63
76
  cli.parse_options(["--mq-tls-cert=#{file}"])
64
- }.to raise_error SystemExit
77
+ }.to raise_error SystemExit, "Certificate file '/path/to/nonexistant/file' not found"
65
78
  end
66
79
  end
67
80
 
@@ -4,23 +4,40 @@ require 'tempfile'
4
4
  describe Hutch::Config do
5
5
  let(:new_value) { 'not-localhost' }
6
6
 
7
+ before do
8
+ Hutch::Config.instance_variable_set(:@config, nil)
9
+ Hutch::Config.initialize
10
+ end
11
+
12
+ after do
13
+ Hutch::Config.instance_variable_set(:@config, nil)
14
+ end
15
+
7
16
  describe '.get' do
8
17
  context 'for valid attributes' do
9
18
  subject { Hutch::Config.get(:mq_host) }
10
19
 
11
20
  context 'with no overridden value' do
12
- it { is_expected.to eq('localhost') }
21
+ it { is_expected.to eq('127.0.0.1') }
13
22
  end
14
23
 
15
24
  context 'with an overridden value' do
16
- before { allow(Hutch::Config).to receive_messages(user_config: { mq_host: new_value }) }
25
+ before do
26
+ Hutch::Config.set(:mq_host, new_value)
27
+ end
28
+
17
29
  it { is_expected.to eq(new_value) }
18
30
  end
19
31
  end
20
32
 
21
33
  context 'for invalid attributes' do
22
- let(:invalid_get) { ->{ Hutch::Config.get(:invalid_attr) } }
23
- specify { expect(invalid_get).to raise_error Hutch::UnknownAttributeError }
34
+ let(:invalid_get) do
35
+ -> { Hutch::Config.get(:invalid_attr) }
36
+ end
37
+
38
+ specify do
39
+ expect(invalid_get).to raise_error Hutch::UnknownAttributeError
40
+ end
24
41
  end
25
42
  end
26
43
 
@@ -35,8 +52,13 @@ describe Hutch::Config do
35
52
  end
36
53
 
37
54
  context 'for invalid attributes' do
38
- let(:invalid_set) { ->{ Hutch::Config.set(:invalid_attr, new_value) } }
39
- specify { expect(invalid_set).to raise_error Hutch::UnknownAttributeError }
55
+ let(:invalid_set) do
56
+ -> { Hutch::Config.set(:invalid_attr, new_value) }
57
+ end
58
+
59
+ specify do
60
+ expect(invalid_set).to raise_error Hutch::UnknownAttributeError
61
+ end
40
62
  end
41
63
  end
42
64
 
@@ -49,9 +71,33 @@ describe Hutch::Config do
49
71
  end
50
72
 
51
73
  context 'for an invalid attribute' do
52
- let(:invalid_getter) { ->{ Hutch::Config.invalid_attr } }
74
+ let(:invalid_getter) { -> { Hutch::Config.invalid_attr } }
53
75
  specify { expect(invalid_getter).to raise_error NoMethodError }
54
76
  end
77
+
78
+ context 'for an ENV-overriden value attribute' do
79
+ around do |example|
80
+ ENV['HUTCH_MQ_HOST'] = 'example.com'
81
+ ENV['HUTCH_MQ_PORT'] = '10001'
82
+ ENV['HUTCH_MQ_TLS'] = 'true'
83
+ example.run
84
+ ENV.delete('HUTCH_MQ_HOST')
85
+ ENV.delete('HUTCH_MQ_PORT')
86
+ ENV.delete('HUTCH_MQ_TLS')
87
+ end
88
+
89
+ it 'returns the override' do
90
+ expect(Hutch::Config.mq_host).to eq 'example.com'
91
+ end
92
+
93
+ it 'returns the override for integers' do
94
+ expect(Hutch::Config.mq_port).to eq 10_001
95
+ end
96
+
97
+ it 'returns the override for booleans' do
98
+ expect(Hutch::Config.mq_tls).to eq true
99
+ end
100
+ end
55
101
  end
56
102
 
57
103
  describe 'a magic setter' do
@@ -63,7 +109,7 @@ describe Hutch::Config do
63
109
  end
64
110
 
65
111
  context 'for an invalid attribute' do
66
- let(:invalid_setter) { ->{ Hutch::Config.invalid_attr = new_value } }
112
+ let(:invalid_setter) { -> { Hutch::Config.invalid_attr = new_value } }
67
113
  specify { expect(invalid_setter).to raise_error NoMethodError }
68
114
  end
69
115
  end
@@ -81,9 +127,9 @@ describe Hutch::Config do
81
127
  context 'when an attribute is invalid' do
82
128
  let(:config_data) { { random_attribute: 'socks' } }
83
129
  it 'raises an error' do
84
- expect {
130
+ expect do
85
131
  Hutch::Config.load_from_file(file)
86
- }.to raise_error(NoMethodError)
132
+ end.to raise_error(NoMethodError)
87
133
  end
88
134
  end
89
135
 
@@ -96,26 +142,21 @@ describe Hutch::Config do
96
142
  expect(Hutch::Config.mq_username).to eq username
97
143
  end
98
144
  end
99
- end
100
145
 
101
- describe '.load_from_file' do
102
- let(:host) { 'localhost' }
103
- let(:username) { 'calvin' }
104
- let(:file) do
105
- Tempfile.new('configs.yaml').tap do |t|
106
- t.write(config_contents)
107
- t.rewind
146
+ context 'when using ERB' do
147
+ let(:host) { 'localhost' }
148
+ let(:file) do
149
+ Tempfile.new('configs.yaml').tap do |t|
150
+ t.write(config_contents)
151
+ t.rewind
152
+ end
108
153
  end
109
- end
110
-
111
- context 'when using ERb' do
112
154
  let(:config_contents) do
113
155
  <<-YAML
114
156
  mq_host: 'localhost'
115
157
  mq_username: '<%= "calvin" %>'
116
158
  YAML
117
159
  end
118
-
119
160
  it 'loads in the config data' do
120
161
  Hutch::Config.load_from_file(file)
121
162
  expect(Hutch::Config.mq_host).to eq host
@@ -123,4 +164,24 @@ YAML
123
164
  end
124
165
  end
125
166
  end
167
+
168
+ context 'developer ergonomics' do
169
+ it 'will accept strings and symbols as config keys' do
170
+ expect(Hutch::Config.get(:mq_host)).to eq '127.0.0.1'
171
+ expect(Hutch::Config.get('mq_host')).to eq '127.0.0.1'
172
+ end
173
+
174
+ describe 'it will not overwrite existing config' do
175
+ it 'with defaults' do
176
+ expect(Hutch::Config.get(:mq_host)).to eq '127.0.0.1'
177
+ Hutch::Config.initialize
178
+
179
+ Hutch::Config.set(:mq_host, 'example2.com')
180
+
181
+ expect(Hutch::Config.get(:mq_host)).to eq 'example2.com'
182
+ Hutch::Config.initialize
183
+ expect(Hutch::Config.get(:mq_host)).to eq 'example2.com'
184
+ end
185
+ end
186
+ end
126
187
  end
@@ -14,21 +14,36 @@ describe Hutch::ErrorHandlers::Airbrake do
14
14
 
15
15
  it "logs the error to Airbrake" do
16
16
  message_id = "1"
17
+ properties = OpenStruct.new(message_id: message_id)
17
18
  payload = "{}"
18
19
  consumer = double
19
20
  ex = error
20
21
  message = {
21
- :error_class => ex.class.name,
22
- :error_message => "#{ ex.class.name }: #{ ex.message }",
23
- :backtrace => ex.backtrace,
24
- :parameters => {
25
- :payload => payload,
26
- :consumer => consumer,
27
- },
28
- :cgi_data => ENV.to_hash,
22
+ payload: payload,
23
+ consumer: consumer,
24
+ cgi_data: ENV.to_hash,
29
25
  }
30
- expect(::Airbrake).to receive(:notify_or_ignore).with(ex, message)
31
- error_handler.handle(message_id, payload, consumer, ex)
26
+ expect(::Airbrake).to receive(:notify).with(ex, message)
27
+ error_handler.handle(properties, payload, consumer, ex)
28
+ end
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)
32
47
  end
33
48
  end
34
49
  end
@@ -14,6 +14,7 @@ describe Hutch::ErrorHandlers::Honeybadger do
14
14
 
15
15
  it "logs the error to Honeybadger" do
16
16
  message_id = "1"
17
+ properties = OpenStruct.new(message_id: message_id)
17
18
  payload = "{}"
18
19
  consumer = double
19
20
  ex = error
@@ -29,8 +30,29 @@ describe Hutch::ErrorHandlers::Honeybadger do
29
30
  :payload => payload
30
31
  }
31
32
  }
32
- expect(::Honeybadger).to receive(:notify_or_ignore).with(message)
33
- error_handler.handle(message_id, payload, consumer, ex)
33
+ expect(error_handler).to receive(:notify_honeybadger).with(message)
34
+ error_handler.handle(properties, payload, consumer, ex)
35
+ end
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)
34
56
  end
35
57
  end
36
58
  end
@@ -4,12 +4,25 @@ describe Hutch::ErrorHandlers::Logger do
4
4
  let(:error_handler) { Hutch::ErrorHandlers::Logger.new }
5
5
 
6
6
  describe '#handle' do
7
+ let(:properties) { OpenStruct.new(message_id: "1") }
8
+ let(:payload) { "{}" }
7
9
  let(:error) { double(message: "Stuff went wrong", class: "RuntimeError",
8
10
  backtrace: ["line 1", "line 2"]) }
9
11
 
10
12
  it "logs three separate lines" do
11
13
  expect(Hutch::Logging.logger).to receive(:error).exactly(3).times
12
- error_handler.handle("1", "{}", double, error)
14
+ error_handler.handle(properties, payload, double, error)
15
+ end
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)
13
26
  end
14
27
  end
15
28
  end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hutch::ErrorHandlers::Opbeat do
4
+ let(:error_handler) { Hutch::ErrorHandlers::Opbeat.new }
5
+
6
+ describe '#handle' do
7
+ let(:properties) { OpenStruct.new(message_id: "1") }
8
+ let(:payload) { "{}" }
9
+ let(:error) do
10
+ begin
11
+ raise "Stuff went wrong"
12
+ rescue RuntimeError => err
13
+ err
14
+ end
15
+ end
16
+
17
+ it "logs the error to Opbeat" do
18
+ expect(Opbeat).to receive(:report).with(error, extra: { payload: payload })
19
+ error_handler.handle(properties, payload, double, error)
20
+ end
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
37
+ end
@@ -4,6 +4,8 @@ describe Hutch::ErrorHandlers::Sentry do
4
4
  let(:error_handler) { Hutch::ErrorHandlers::Sentry.new }
5
5
 
6
6
  describe '#handle' do
7
+ let(:properties) { OpenStruct.new(message_id: "1") }
8
+ let(:payload) { "{}" }
7
9
  let(:error) do
8
10
  begin
9
11
  raise "Stuff went wrong"
@@ -12,9 +14,24 @@ describe Hutch::ErrorHandlers::Sentry do
12
14
  end
13
15
  end
14
16
 
17
+ it "logs the error to Sentry" do
18
+ expect(Raven).to receive(:capture_exception).with(error, extra: { payload: payload })
19
+ error_handler.handle(properties, payload, double, error)
20
+ end
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
+
15
32
  it "logs the error to Sentry" do
16
33
  expect(Raven).to receive(:capture_exception).with(error)
17
- error_handler.handle("1", "{}", double, error)
34
+ error_handler.handle_setup_exception(error)
18
35
  end
19
36
  end
20
37
  end
@@ -3,24 +3,30 @@ require 'spec_helper'
3
3
  describe Hutch::Logging do
4
4
  let(:dummy_object) do
5
5
  class DummyObject
6
- include Hutch::Logging
6
+ include described_class
7
7
  end
8
8
  end
9
9
 
10
10
  describe '#logger' do
11
+ around do |example|
12
+ old_logger = described_class.logger
13
+ described_class.setup_logger
14
+ example.run
15
+ described_class.logger = old_logger
16
+ end
17
+
11
18
  context 'with the default logger' do
12
- subject { Hutch::Logging.logger }
19
+ subject { described_class.logger }
13
20
 
14
21
  it { is_expected.to be_instance_of(Logger) }
15
22
  end
16
23
 
17
24
  context 'with a custom logger' do
18
- let(:dummy_logger) { double("Dummy logger", warn: true, info: true) }
19
- after { Hutch::Logging.setup_logger }
25
+ let(:dummy_logger) { double("Dummy logger") }
20
26
 
21
27
  it "users the custom logger" do
22
- Hutch::Logging.logger = dummy_logger
23
- expect(Hutch::Logging.logger).to eq(dummy_logger)
28
+ described_class.logger = dummy_logger
29
+ expect(described_class.logger).to eq(dummy_logger)
24
30
  end
25
31
  end
26
32
  end
@@ -0,0 +1,51 @@
1
+ require 'hutch/waiter'
2
+
3
+ RSpec.describe Hutch::Waiter do
4
+ describe '.wait_until_signaled' do
5
+ let(:pid) { Process.pid }
6
+ def start_kill_thread(signal)
7
+ Thread.new do
8
+ # sleep allows the worker time to set up the signal handling
9
+ # before the kill signal is sent.
10
+ sleep 0.001
11
+ Process.kill signal, pid
12
+ end
13
+ end
14
+
15
+ context 'a QUIT signal is received', if: RSpec::Support::Ruby.mri? do
16
+ it 'logs that hutch is stopping' do
17
+ expect(Hutch::Logging.logger).to receive(:info)
18
+ .with('caught SIGQUIT, stopping hutch...')
19
+
20
+ start_kill_thread('QUIT')
21
+ described_class.wait_until_signaled
22
+ end
23
+ end
24
+
25
+ context 'a TERM signal is received' do
26
+ it 'logs that hutch is stopping' do
27
+ expect(Hutch::Logging.logger).to receive(:info)
28
+ .with('caught SIGTERM, stopping hutch...')
29
+
30
+ start_kill_thread('TERM')
31
+ described_class.wait_until_signaled
32
+ end
33
+ end
34
+
35
+ context 'a INT signal is received' do
36
+ it 'logs that hutch is stopping' do
37
+ expect(Hutch::Logging.logger).to receive(:info)
38
+ .with('caught SIGINT, stopping hutch...')
39
+
40
+ start_kill_thread('INT')
41
+ described_class.wait_until_signaled
42
+ end
43
+ end
44
+ end
45
+
46
+ describe described_class::SHUTDOWN_SIGNALS do
47
+ it 'includes only things in Signal.list.keys' do
48
+ expect(described_class).to eq(described_class & Signal.list.keys)
49
+ end
50
+ end
51
+ end