rack-ecg 0.0.4 → 0.0.5

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
  SHA1:
3
- metadata.gz: b3d3e3118a76ef6c60141ec590ea0feb00781a65
4
- data.tar.gz: eb8a0b817ab97ce981828fa8c465b60806fcf978
3
+ metadata.gz: 7bd8eb96d04ad56746e1b5a29c1bf994706a4fe1
4
+ data.tar.gz: c64c43e74caa9020f24fab71f72a328cb3a194c6
5
5
  SHA512:
6
- metadata.gz: 76a8c2d9eaf627ee8c4277fe564a0712ae05347237652fb26b8950ecfe51e5aeecbb084a72516d45a9e347ef66bc6e8db657e12eb9444376581fafdfbbb1c8a0
7
- data.tar.gz: 40baa754d837d5ddf996a43604d6e2ed5693a74bf488f01f1b45f7234e60bd53f2e79b073583d66f3f9031d7d1edf729dbffc6126489affc6fd5c1fee7ad03df
6
+ metadata.gz: fc8fc08610d5e269889dc0d5f6717be7b468298f7469f2fa01754be67988aa242bbd0dd5b78ae9f5003d4c096800c2444a6e1d9eadd325a05d9fe71450f9af75
7
+ data.tar.gz: e173e7a8cf827fe3dd3d747afd8cb9d2440e64ec67d44fada6aaf9bf330e08953f78b69eb945fb4d30dba2fffedf98ad675d621423f0ae89f0f98ac7463b6d4e
data/README.md CHANGED
@@ -126,6 +126,56 @@ $ curl http://localhost:9292/_ecg
126
126
  }
127
127
  ```
128
128
 
129
+ #### Checks with parameters
130
+ Some checks, such as the `sequel` check, require a parameter hash. In this case, you must provide the check as a tuple consisting of both the check name, and a hash of parameters:
131
+
132
+ ```ruby
133
+ use Rack::ECG, checks: [:http, [:sequel, {connection: "sqlite://my-sqlite.db"}]]
134
+ ```
135
+
136
+ ```
137
+ $ curl http://localhost:9292/_ecg
138
+ {
139
+ "http": {
140
+ "status": "ok",
141
+ "value": "online"
142
+ },
143
+ "sequel": {
144
+ "status": "ok",
145
+ "value": "true"
146
+ }
147
+ }
148
+ ```
149
+
150
+ Because the `sequel` check operates on a per-connection basis, you can specify multiple Sequel databases to independently check, and provide a friendly name for disambiguation purposes:
151
+
152
+ ```ruby
153
+ use Rack::ECG, checks: [
154
+ :http,
155
+ [:sequel, {connection: 'sqlite://events.db', name: 'events'}],
156
+ [:sequel, {connection: 'sqlite://projections.db', name: 'projections'}]
157
+ ]
158
+ ```
159
+
160
+ ```
161
+ $ curl http://localhost:9292/_ecg
162
+
163
+ {
164
+ "http": {
165
+ "status": "ok",
166
+ "value": "online"
167
+ },
168
+ "sequel_events": {
169
+ "status": "ok",
170
+ "value": "true"
171
+ },
172
+ "sequel_projections": {
173
+ "status": "ok",
174
+ "value": "true"
175
+ }
176
+ }
177
+ ```
178
+
129
179
  ### `at`
130
180
 
131
181
  By default `Rack::ECG` is mapped to a URL of `/_ecg`, you can set this to
@@ -188,7 +238,7 @@ For larger new features: Do everything as above, but first also make contact wit
188
238
 
189
239
  ## About
190
240
 
191
- This project is maintained by the [Envato engineering team][webuild] and funded by [Envato][envato].
241
+ This project is maintained by the [Envato engineering team][webuild] and funded by [Envato][envato].
192
242
 
193
243
  [<img src="http://opensource.envato.com/images/envato-oss-readme-logo.png" alt="Envato logo">][envato]
194
244
 
@@ -0,0 +1,11 @@
1
+ require 'rack/ecg'
2
+ require 'sequel'
3
+ require 'sqlite3'
4
+
5
+ use Rack::ECG, checks: [
6
+ :http,
7
+ [:sequel, {connection: 'sqlite://events.db', name: 'events'}],
8
+ [:sequel, {connection: 'sqlite://projections.db', name: 'projections'}]
9
+ ]
10
+
11
+ run -> (env) { [200, {}, ["Hello, World"]] }
@@ -1,7 +1,7 @@
1
1
  require "rack/ecg/version"
2
2
  require "json"
3
3
  require "open3"
4
- require "rack/ecg/check"
4
+ require "rack/ecg/check_factory"
5
5
 
6
6
  module Rack
7
7
  class ECG
@@ -11,9 +11,8 @@ module Rack
11
11
  def initialize(app=nil, options={})
12
12
  @app = app
13
13
 
14
- check_names = options.delete(:checks) || []
15
- @check_classes = build_check_classes(check_names)
16
-
14
+ check_configuration = options.delete(:checks) || []
15
+ @check_factory = CheckFactory.new(check_configuration, DEFAULT_CHECKS)
17
16
  @at = options.delete(:at) || DEFAULT_MOUNT_AT
18
17
 
19
18
  @hook = options.delete(:hook)
@@ -21,13 +20,11 @@ module Rack
21
20
 
22
21
  def call(env)
23
22
  if env["PATH_INFO"] == @at
24
-
25
- check_results = @check_classes.inject({}){|results, check_class|
26
- check = check_class.new
23
+ check_results = @check_factory.build_all.inject({}) do |results, check|
27
24
  results.merge(check.result.to_json)
28
- }
25
+ end
29
26
 
30
- success = check_results.none? { |check| check[1][:status] == "error" }
27
+ success = check_results.none? { |check| check[1][:status] == Check::Status::ERROR }
31
28
 
32
29
  response_status = success ? 200 : 500
33
30
 
@@ -47,16 +44,5 @@ module Rack
47
44
  [404, {},[]]
48
45
  end
49
46
  end
50
-
51
- private
52
- def build_check_classes(check_names)
53
- check_names = Array(check_names) # handle nil, or not a list
54
- check_names = check_names | DEFAULT_CHECKS # add the :http check if it's not there
55
- check_names.map{|check_name|
56
- check_class = CheckRegistry.instance[check_name]
57
- raise "Don't know about check #{check_name}" unless check_class
58
- check_class
59
- }
60
- end
61
47
  end
62
48
  end
@@ -5,10 +5,16 @@ require "rack/ecg/check/http"
5
5
  require "rack/ecg/check/migration_version"
6
6
  require "rack/ecg/check/active_record_connection"
7
7
  require "rack/ecg/check/redis_connection"
8
+ require "rack/ecg/check/sequel_connection"
8
9
 
9
10
  module Rack
10
11
  class ECG
11
12
  module Check
13
+ module Status
14
+ OK = "ok".freeze
15
+ ERROR = "error".freeze
16
+ end
17
+
12
18
  class Result < Struct.new(:name, :status, :value)
13
19
  def to_json
14
20
  {name => {:status => status, :value => value}}
@@ -4,17 +4,17 @@ module Rack
4
4
  class ActiveRecordConnection
5
5
  def result
6
6
  value = ""
7
- status = "ok"
7
+ status = Status::OK
8
8
  begin
9
9
  if defined?(ActiveRecord)
10
10
  value = ::ActiveRecord::Base.connection.active?
11
- status = value ? "ok" : "error"
11
+ status = value ? Status::OK : Status::ERROR
12
12
  else
13
- status = "error"
13
+ status = Status::ERROR
14
14
  value = "ActiveRecord not found"
15
15
  end
16
16
  rescue => e
17
- status = "error"
17
+ status = Status::ERROR
18
18
  value = e.message
19
19
  end
20
20
 
@@ -5,7 +5,7 @@ module Rack
5
5
  # this is basically a "hello-world"
6
6
  class Error
7
7
  def result
8
- Result.new(:error, "error", "PC LOAD LETTER")
8
+ Result.new(:error, Status::ERROR, "PC LOAD LETTER")
9
9
  end
10
10
  end
11
11
 
@@ -7,7 +7,7 @@ module Rack
7
7
 
8
8
  success = wait_thread.value.success?
9
9
 
10
- status = success ? "ok" : "error"
10
+ status = success ? Status::OK : Status::ERROR
11
11
 
12
12
  value = success ? stdout.read : stderr.read
13
13
  value = value.strip
@@ -5,7 +5,7 @@ module Rack
5
5
  # this is basically a "hello-world"
6
6
  class Http
7
7
  def result
8
- Result.new(:http, "ok", "online")
8
+ Result.new(:http, Status::OK, "online")
9
9
  end
10
10
  end
11
11
 
@@ -4,17 +4,17 @@ module Rack
4
4
  class MigrationVersion
5
5
  def result
6
6
  value = ""
7
- status = "ok"
7
+ status = Status::OK
8
8
  begin
9
9
  if defined?(ActiveRecord)
10
10
  connection = ActiveRecord::Base.connection
11
11
  value = connection.select_value("select max(version) from schema_migrations")
12
12
  else
13
- status = "error"
13
+ status = Status::ERROR
14
14
  value = "ActiveRecord not found"
15
15
  end
16
16
  rescue => e
17
- status = "error"
17
+ status = Status::ERROR
18
18
  value = e.message
19
19
  end
20
20
 
@@ -4,17 +4,17 @@ module Rack
4
4
  class RedisConnection
5
5
  def result
6
6
  value = ""
7
- status = "ok"
7
+ status = Status::OK
8
8
  begin
9
9
  if defined?(::Redis)
10
10
  value = ::Redis.current.connected?
11
- status = value ? "ok" : "error"
11
+ status = value ? Status::OK : Status::ERROR
12
12
  else
13
- status = "error"
13
+ status = Status::ERROR
14
14
  value = "Redis not found"
15
15
  end
16
16
  rescue => e
17
- status = "error"
17
+ status = Status::ERROR
18
18
  value = e.message
19
19
  end
20
20
 
@@ -0,0 +1,47 @@
1
+ module Rack
2
+ class ECG
3
+ module Check
4
+ class SequelConnection
5
+ attr_reader :connection_parameters, :name
6
+ def initialize(parameters = {})
7
+ @connection_parameters = parameters[:connection]
8
+ @name = parameters[:name]
9
+ end
10
+
11
+ def result
12
+ value = ""
13
+ status = Status::OK
14
+ begin
15
+ if connection_parameters.nil?
16
+ status = Status::ERROR
17
+ value = "Sequel Connection parameters not found"
18
+ elsif defined?(::Sequel)
19
+ ::Sequel.connect(connection_parameters) { |db|
20
+ value = db.test_connection
21
+ status = Status::OK
22
+ }
23
+ else
24
+ status = Status::ERROR
25
+ value = "Sequel not found"
26
+ end
27
+ rescue => e
28
+ status = Status::ERROR
29
+ value = e.message
30
+ end
31
+
32
+ Result.new(result_key.to_sym, status, value.to_s)
33
+ end
34
+
35
+ def result_key
36
+ if name
37
+ "sequel #{name.downcase}".gsub(/\W+/, '_')
38
+ else
39
+ "sequel"
40
+ end
41
+ end
42
+
43
+ CheckRegistry.instance.register(:sequel, SequelConnection)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ require "rack/ecg/check"
2
+
3
+ module Rack
4
+ class ECG
5
+ class CheckFactory
6
+ CheckDefinition = Struct.new(:check_class, :parameters)
7
+
8
+ def initialize(definitions, default_checks = [])
9
+ definitions = Array(definitions) | default_checks
10
+
11
+ @checks = definitions.map do |check_name, check_parameters|
12
+ CheckDefinition.new(CheckRegistry.lookup(check_name), check_parameters)
13
+ end
14
+ end
15
+
16
+ def build_all
17
+ @checks.map do |check_definition|
18
+ build(check_definition.check_class, check_definition.parameters)
19
+ end
20
+ end
21
+
22
+ def build(check_class, parameters = nil)
23
+ parameters.nil? ? check_class.new : check_class.new(parameters)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -3,6 +3,7 @@ require "singleton"
3
3
  module Rack
4
4
  class ECG
5
5
  class CheckRegistry
6
+ CheckNotRegistered = Class.new(StandardError)
6
7
  include Singleton
7
8
 
8
9
  def initialize()
@@ -13,8 +14,16 @@ module Rack
13
14
  @registry[name] = check_class
14
15
  end
15
16
 
16
- def [](name)
17
- @registry[name]
17
+ def lookup(name)
18
+ @registry.fetch(name) { raise CheckNotRegistered.new("Check '#{name}' is not registered") }
19
+ end
20
+
21
+ def self.lookup(name)
22
+ instance.lookup(name)
23
+ end
24
+
25
+ def self.register(name, check_class)
26
+ instance.register(name, check_class)
18
27
  end
19
28
  end
20
29
  end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class ECG
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
@@ -0,0 +1,59 @@
1
+ RSpec.describe Rack::ECG::CheckFactory do
2
+ class MyCheckClass; end
3
+ class MyOtherCheckClass; def initialize(params); end; end
4
+
5
+ let(:definitions) { [] }
6
+ let(:default_checks) { [] }
7
+ subject(:check_factory) { Rack::ECG::CheckFactory.new(definitions, default_checks) }
8
+
9
+ describe "#build" do
10
+ context "with a class that does not take params" do
11
+ let(:check_class) { spy(MyCheckClass) }
12
+
13
+ it "builds the specified class" do
14
+ expect { check_factory.build(check_class) }.not_to raise_error
15
+ expect(check_class).to have_received(:new).with(no_args)
16
+ end
17
+ end
18
+
19
+ context "with a class that does not take params" do
20
+ let(:check_class) { spy(MyOtherCheckClass) }
21
+ let(:check_parameters) { double }
22
+ it "builds the specified class" do
23
+ expect { check_factory.build(check_class, check_parameters) }.not_to raise_error
24
+ expect(check_class).to have_received(:new).with(check_parameters)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "#build_all" do
30
+ context "with defined checks" do
31
+ let(:definitions) { [:my_check, [:my_other_check, {foo: 'bar'}]] }
32
+ let(:check_class) { spy(MyCheckClass) }
33
+ let(:other_check_class) { spy(MyOtherCheckClass) }
34
+ before do
35
+ allow(Rack::ECG::CheckRegistry).to receive(:lookup).with(:my_check).and_return(check_class)
36
+ allow(Rack::ECG::CheckRegistry).to receive(:lookup).with(:my_other_check).and_return(other_check_class)
37
+ end
38
+
39
+ it "builds all registered checks" do
40
+ check_factory.build_all
41
+ expect(check_class).to have_received(:new).with(no_args)
42
+ expect(other_check_class).to have_received(:new).with(foo: 'bar')
43
+ end
44
+ end
45
+
46
+ context "with defined default checks" do
47
+ let(:default_checks) { [:http] }
48
+ let(:http_class) { spy(Rack::ECG::Check::Http) }
49
+ before do
50
+ allow(Rack::ECG::CheckRegistry).to receive(:lookup).with(:http).and_return(http_class)
51
+ end
52
+
53
+ it "builds registered default checks" do
54
+ check_factory.build_all
55
+ expect(http_class).to have_received(:new).with(no_args)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,23 @@
1
+
2
+ RSpec.describe Rack::ECG::CheckRegistry do
3
+ class MyCheckClass; end
4
+ subject(:check_registry) { described_class }
5
+
6
+ before do
7
+ check_registry.register(:my_check, MyCheckClass)
8
+ end
9
+
10
+ describe ".lookup" do
11
+ context "with a registered class" do
12
+ it "returns the registered class" do
13
+ expect(check_registry.lookup(:my_check)).to eq(MyCheckClass)
14
+ end
15
+ end
16
+
17
+ context "when the class is not registered" do
18
+ it "raises an error" do
19
+ expect { check_registry.lookup(:my_other_check) }.to raise_error(Rack::ECG::CheckRegistry::CheckNotRegistered)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -129,6 +129,9 @@ RSpec.describe "when used as middleware" do
129
129
  let(:options) {
130
130
  { checks: [:migration_version] }
131
131
  }
132
+ let(:connection) { double("connection") }
133
+ let(:version) { "123456" }
134
+
132
135
  context "when available" do
133
136
  it "is reported" do
134
137
  class ActiveRecord
@@ -137,8 +140,6 @@ RSpec.describe "when used as middleware" do
137
140
  end
138
141
  end
139
142
  end
140
- version = "123456"
141
- connection = double("connection")
142
143
  expect(ActiveRecord::Base).to receive(:connection).and_return(connection)
143
144
  expect(connection).to receive(:select_value).
144
145
  with("select max(version) from schema_migrations").
@@ -164,6 +165,8 @@ RSpec.describe "when used as middleware" do
164
165
  { checks: [:active_record] }
165
166
  }
166
167
  context "when available" do
168
+ let(:active) { true }
169
+ let(:connection) { double("connection") }
167
170
  it "is reported" do
168
171
  class ActiveRecord
169
172
  class Base
@@ -171,8 +174,6 @@ RSpec.describe "when used as middleware" do
171
174
  end
172
175
  end
173
176
  end
174
- active = true
175
- connection = double("connection")
176
177
  expect(ActiveRecord::Base).to receive(:connection).and_return(connection)
177
178
  expect(connection).to receive(:active?).and_return(active)
178
179
  get "/_ecg"
@@ -196,13 +197,13 @@ RSpec.describe "when used as middleware" do
196
197
  { checks: [:redis] }
197
198
  }
198
199
  context "when available" do
200
+ let(:instance) { double("current") }
201
+ let(:connected) { true }
199
202
  it "is reported" do
200
203
  class Redis
201
204
  def self.current
202
205
  end
203
206
  end
204
- connected = true
205
- instance = double("current")
206
207
  expect(Redis).to receive(:current).and_return(instance)
207
208
  expect(instance).to receive(:connected?).and_return(connected)
208
209
  get "/_ecg"
@@ -220,5 +221,26 @@ RSpec.describe "when used as middleware" do
220
221
  end
221
222
  end
222
223
  end
224
+
225
+ context "sequel" do
226
+ let(:options) {
227
+ { checks: [[:sequel, {name: 'My Awesome DB', connection: 'sqlite://'}]] }
228
+ }
229
+ let(:instance) { double("sequel_db") }
230
+
231
+ context "when available" do
232
+ it "is reported" do
233
+ class Sequel
234
+ def self.connect(_)
235
+ end
236
+ end
237
+ expect(Sequel).to receive(:connect).with('sqlite://').and_yield(instance)
238
+ expect(instance).to receive(:test_connection).and_return(true)
239
+ get "/_ecg"
240
+ expect(json_body["sequel_my_awesome_db"]["status"]).to eq("ok")
241
+ expect(json_body["sequel_my_awesome_db"]["value"]).to eq("true")
242
+ end
243
+ end
244
+ end
223
245
  end
224
246
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-ecg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Envato
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-05-04 00:00:00.000000000 Z
12
+ date: 2017-05-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -115,6 +115,7 @@ files:
115
115
  - examples/basic.ru
116
116
  - examples/checks.ru
117
117
  - examples/mounted_path.ru
118
+ - examples/parameters.ru
118
119
  - examples/stand_alone.ru
119
120
  - gemfiles/rack_v1.gemfile
120
121
  - lib/rack-ecg.rb
@@ -126,9 +127,13 @@ files:
126
127
  - lib/rack/ecg/check/http.rb
127
128
  - lib/rack/ecg/check/migration_version.rb
128
129
  - lib/rack/ecg/check/redis_connection.rb
130
+ - lib/rack/ecg/check/sequel_connection.rb
131
+ - lib/rack/ecg/check_factory.rb
129
132
  - lib/rack/ecg/check_registry.rb
130
133
  - lib/rack/ecg/version.rb
131
134
  - rack-ecg.gemspec
135
+ - spec/check_factory_spec.rb
136
+ - spec/check_registry_spec.rb
132
137
  - spec/rack_middleware_spec.rb
133
138
  - spec/spec_helper.rb
134
139
  homepage: https://github.com/envato/rack-ecg
@@ -151,10 +156,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
156
  version: '0'
152
157
  requirements: []
153
158
  rubyforge_project:
154
- rubygems_version: 2.5.2
159
+ rubygems_version: 2.6.10
155
160
  signing_key:
156
161
  specification_version: 4
157
162
  summary: Rack middleware serving a health check page
158
163
  test_files:
164
+ - spec/check_factory_spec.rb
165
+ - spec/check_registry_spec.rb
159
166
  - spec/rack_middleware_spec.rb
160
167
  - spec/spec_helper.rb