hutch 0.21.0-java → 0.25.0-java

Sign up to get free protection for your applications and to get access to all the features.
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