sinatra-health-check 0.1.0 → 0.2.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: 334bafa6e4d41f6204c4b8dd3c85c9ae0bcd7197
4
- data.tar.gz: 177f9eb36abcbc83d387f6cf2893d60853bc45e4
3
+ metadata.gz: 629086796b22da38bdfb0bb9424c6a948139c4f8
4
+ data.tar.gz: 46c9a99be29204445dedd53de8cd3a57f6093c6e
5
5
  SHA512:
6
- metadata.gz: 31e2492861672eb3e93bd0ebfce9ec1ae0c635d74dca19c9b10a85ccb8064cc14eef85aaf6ee57312e91912a5fc99120db96aa8c023f455f131e06b888ef5c93
7
- data.tar.gz: 2cc30092da95700d7c10710b0594c97257c162518a0a9030ec364364b7c0466f842746870d3fd5119681615158d4006b051694e8f5423231b3065b36eadd5f4b
6
+ metadata.gz: 3a017a22ffb6da52096e0c35ff275f128407c47aaa17e4c06c82033da6a65d6641b6b14189e75882f2e10f9951cec0fe7e3e541a99e74b736c4e661d8b9c7f96
7
+ data.tar.gz: 1d6db01bbd831a451492957f5e978a977b6cc6b8a672329285d06460f4af2818f23adb18a531087af9d70db1cb7f01e4633a9b7979cc9a91b0e120b24d8833b4
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Otto (GmbH & Co KG)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
@@ -1,4 +1,9 @@
1
1
  module SinatraHealthCheck
2
2
  require_relative 'sinatra-health-check/version'
3
+ require_relative 'sinatra-health-check/status'
4
+ require_relative 'sinatra-health-check/status/aggregated'
5
+ require_relative 'sinatra-health-check/status/overwriting_aggregator'
6
+ require_relative 'sinatra-health-check/status/forgiving_aggregator'
7
+ require_relative 'sinatra-health-check/status/strict_aggregator'
3
8
  require_relative 'sinatra-health-check/checker'
4
9
  end
@@ -3,29 +3,36 @@
3
3
  class SinatraHealthCheck::Checker
4
4
 
5
5
  DEFAULT_OPTS = {
6
- :exit => true,
7
- :health => true,
8
- :logger => nil,
9
- :signals => %w[TERM INT],
10
- :timeout => 10,
11
- :wait => 0,
6
+ :aggregator => SinatraHealthCheck::Status::StrictAggregator.new,
7
+ :exit => true,
8
+ :health => true,
9
+ :logger => nil,
10
+ :signals => %w[TERM INT],
11
+ :systems => {},
12
+ :timeout => 10,
13
+ :wait => 0,
12
14
  }
13
15
 
14
16
  require 'thread'
15
17
 
16
18
  attr_accessor :health
17
- alias :healthy? :health
19
+ attr_reader :systems
18
20
 
19
21
  # Create a health checker.
20
22
  # Params:
23
+ # ++aggrgator++: an aggregator for substatuus, default: StrictAggregator
21
24
  # ++exit++: call ++exit++ at the end of ++graceful_stop++
22
25
  # ++health++: initial health state
23
26
  # ++logger++: a logger
24
27
  # ++signals++: array of signals to register a graceful stop handler
28
+ # ++systems++: a hash of subsystems responding to .status
25
29
  # ++timeout++: timeout for graceful stop in seconds
26
- def initialize(opts = DEFAULT_OPTS)
30
+ # ++wait++: wait before setting health to unhealthy
31
+ def initialize(opts = {})
27
32
  @opts = DEFAULT_OPTS.merge(opts)
33
+ @aggregator = SinatraHealthCheck::Status::OverwritingAggregator.new(@opts[:aggregator])
28
34
  @health = @opts[:health]
35
+ @systems = @opts[:systems]
29
36
  trap(@opts[:signals])
30
37
  end
31
38
 
@@ -53,6 +60,17 @@ class SinatraHealthCheck::Checker
53
60
  @stopper.join if @stopper
54
61
  end
55
62
 
63
+ # Returns a Status object
64
+ def status
65
+ statuus = {}
66
+ systems.each { |k,v| statuus[k] = v.status if v.respond_to?(:status) }
67
+ @aggregator.aggregate(statuus, health ? nil : SinatraHealthCheck::Status.new(:error, 'app is unhealthy'))
68
+ end
69
+
70
+ def healthy?
71
+ status.level != :error
72
+ end
73
+
56
74
  private
57
75
 
58
76
  def logger
@@ -0,0 +1,37 @@
1
+ # Application status definition
2
+ class SinatraHealthCheck::Status
3
+
4
+ SEVERITIES = {
5
+ :ok => 0,
6
+ :warning => 1,
7
+ :error => 2,
8
+ }
9
+
10
+ attr_reader :level, :message, :extras
11
+
12
+ def initialize(level, message, extras = {})
13
+ raise ArgumentError, "level must be one of #{SEVERITIES.keys.join(', ')}, but is #{level}" unless SEVERITIES[level]
14
+ raise ArgumentError, "message must not be nil" unless message
15
+ raise ArgumentError, "extras must be a hash, but is #{extras.class}" unless extras.is_a?(Hash)
16
+
17
+ @level = level
18
+ @message = message
19
+ @extras = extras
20
+ end
21
+
22
+ def to_i
23
+ SEVERITIES[level]
24
+ end
25
+ alias :severity :to_i
26
+
27
+ def to_h
28
+ {
29
+ :status => level.to_s.upcase,
30
+ :message => message,
31
+ }.merge(extras)
32
+ end
33
+
34
+ def to_json
35
+ to_h.to_json
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # Application status definition with subsystems
2
+ class SinatraHealthCheck::Status::Aggregated < SinatraHealthCheck::Status
3
+
4
+ attr_reader :statuus
5
+
6
+ def initialize(level, message, statuus, extras = {})
7
+ raise ArgumentError, "statuus must be a hash of SinatraHealthCheck::Status, but is #{statuus.class}" \
8
+ unless statuus.is_a?(Hash)
9
+ super(level, message, { :statusDetails => statuus }.merge(extras))
10
+ end
11
+
12
+ def to_h
13
+ subs = {}
14
+ extras[:statusDetails].each { |k,v| subs[k] = v.to_h }
15
+
16
+ s = extras.merge({
17
+ :status => level.to_s.upcase,
18
+ :message => message,
19
+ :statusDetails => subs
20
+ })
21
+ s.delete(:statusDetails) if s[:statusDetails].size == 0
22
+ s
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ # Aggregate sub statuus, best wins
2
+ class SinatraHealthCheck::Status::ForgivingAggregator
3
+ def aggregate(statuus)
4
+ status = statuus.values.min_by { |s| s.to_i } || SinatraHealthCheck::Status.new(:ok, 'everything is fine')
5
+ message = status.level == :ok ? 'everything is fine' : "all statuus are at least #{status.level}"
6
+
7
+ SinatraHealthCheck::Status::Aggregated.new(status.level, message, statuus)
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ # Aggregate statuus with an aggregator but allow overwriting :level and :message.
2
+ class SinatraHealthCheck::Status::OverwritingAggregator
3
+ def initialize(aggregator)
4
+ raise ArgumentError, 'aggregator must respond to .aggregate' unless aggregator.respond_to?(:aggregate)
5
+ @aggregator = aggregator
6
+ end
7
+
8
+ # aggregate statuus with given aggregator, but overwrite :status and :message if overwrite is given too
9
+ def aggregate(statuus, overwrite = nil)
10
+ if overwrite
11
+ SinatraHealthCheck::Status::Aggregated.new(overwrite.level, overwrite.message, statuus)
12
+ else
13
+ @aggregator.aggregate(statuus)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ # Aggregate sub statuus, worst wins
2
+ class SinatraHealthCheck::Status::StrictAggregator
3
+ def aggregate(statuus)
4
+ status = statuus.values.max_by { |s| s.to_i } || SinatraHealthCheck::Status.new(:ok, 'everything is fine')
5
+ message = status.level == :ok ? 'everything is fine' : "at least one status is #{status.level}"
6
+
7
+ SinatraHealthCheck::Status::Aggregated.new(status.level, message, statuus)
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module SinatraHealthCheck
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -56,4 +56,29 @@ describe SinatraHealthCheck::Checker do
56
56
  end
57
57
  end
58
58
 
59
+ context '#status' do
60
+ it 'has a status' do
61
+ expect(subject.status.level).to eq(:ok)
62
+ expect(subject.healthy?).to be_truthy
63
+ expect(subject.status.to_h[:statusDetails]).to be_nil
64
+ end
65
+
66
+ it 'has substatus' do
67
+ substatus = SinatraHealthCheck::Status.new(:ok, 'sub is ok')
68
+ system = double("System", :status => substatus)
69
+ subject.systems[:sub] = system
70
+ expect(subject.status.level).to eq(:ok)
71
+ expect(subject.healthy?).to be_truthy
72
+ expect(subject.status.to_h[:statusDetails][:sub]).to eq(substatus.to_h)
73
+ end
74
+
75
+ it 'is unhealthy with unhealthy subsystem' do
76
+ substatus = SinatraHealthCheck::Status.new(:error, 'unhealthy')
77
+ system = double("System", :status => substatus)
78
+ subject.systems[:sub] = system
79
+ expect(subject.status.level).to eq(:error)
80
+ expect(subject.healthy?).to be_falsey
81
+ end
82
+ end
83
+
59
84
  end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe SinatraHealthCheck::Status::ForgivingAggregator do
4
+
5
+ context '#aggregate' do
6
+
7
+ ONE = SinatraHealthCheck::Status.new(:error, 'foo')
8
+ TWO = SinatraHealthCheck::Status.new(:warning, 'bar')
9
+ THREE = SinatraHealthCheck::Status.new(:ok, 'foobar')
10
+
11
+ it 'aggregates {} to :ok' do
12
+ expect(subject.aggregate({}).level).to eq(:ok)
13
+ end
14
+
15
+ it 'aggregates to :ok when one status is :ok' do
16
+ statuus = {
17
+ :one => ONE,
18
+ :two => TWO,
19
+ :three => THREE,
20
+ }
21
+ expect(subject.aggregate(statuus).level).to eq(:ok)
22
+ end
23
+
24
+ it 'aggregates to :warning when one status is :warning' do
25
+ statuus = {
26
+ :one => ONE,
27
+ :two => TWO,
28
+ }
29
+ expect(subject.aggregate(statuus).level).to eq(:warning)
30
+ end
31
+
32
+ it 'aggregates to :error when all status are :error' do
33
+ statuus = {
34
+ :one => ONE,
35
+ }
36
+ expect(subject.aggregate(statuus).level).to eq(:error)
37
+ end
38
+
39
+ it 'aggregates details' do
40
+ statuus = {
41
+ :one => ONE,
42
+ :two => TWO,
43
+ }
44
+ expect(subject.aggregate(statuus).to_h[:statusDetails]).to eq({
45
+ :one => {
46
+ :status => 'ERROR',
47
+ :message => 'foo',
48
+ },
49
+ :two => {
50
+ :status => 'WARNING',
51
+ :message => 'bar',
52
+ },
53
+ })
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe SinatraHealthCheck::Status::OverwritingAggregator do
4
+
5
+ context '#aggregate' do
6
+ aggregator = SinatraHealthCheck::Status::ForgivingAggregator.new
7
+ subject { described_class.new(aggregator) }
8
+
9
+ it 'overwrites the status' do
10
+ expect(aggregator).not_to receive(:aggregate)
11
+ expect(subject.aggregate({}, SinatraHealthCheck::Status.new(:error, 'foo')).level).to eq(:error)
12
+ end
13
+
14
+ it 'does not overwrite the status' do
15
+ expect(aggregator).to receive(:aggregate) { :foo }
16
+ expect(subject.aggregate({})).to eq(:foo)
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe SinatraHealthCheck::Status do
4
+
5
+ describe '#init' do
6
+ subject { described_class.new(:ok, 'fooo')}
7
+ its(:level) { should == :ok }
8
+ its(:message) { should == 'fooo' }
9
+ its(:to_i) { should == 0 }
10
+ its(:to_h) { should == { :status => 'OK', :message => 'fooo' } }
11
+ its(:to_json) { should == { :status => 'OK', :message => 'fooo' }.to_json }
12
+ end
13
+
14
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe SinatraHealthCheck::Status::StrictAggregator do
4
+
5
+ context '#aggregate' do
6
+
7
+ ONE = SinatraHealthCheck::Status.new(:error, 'foo')
8
+ TWO = SinatraHealthCheck::Status.new(:warning, 'bar')
9
+ THREE = SinatraHealthCheck::Status.new(:ok, 'foobar')
10
+
11
+ it 'aggregates {} to :ok' do
12
+ expect(subject.aggregate({}).level).to eq(:ok)
13
+ end
14
+
15
+ it 'aggregates to :ok when one status is :ok' do
16
+ statuus = {
17
+ :one => ONE,
18
+ :two => TWO,
19
+ :three => THREE,
20
+ }
21
+ expect(subject.aggregate(statuus).level).to eq(:error)
22
+ end
23
+
24
+ it 'aggregates to :warning when one status is :warning' do
25
+ statuus = {
26
+ :two => TWO,
27
+ :three => THREE,
28
+ }
29
+ expect(subject.aggregate(statuus).level).to eq(:warning)
30
+ end
31
+
32
+ it 'aggregates to :error when all status are :error' do
33
+ statuus = {
34
+ :three => THREE,
35
+ }
36
+ expect(subject.aggregate(statuus).level).to eq(:ok)
37
+ end
38
+
39
+ it 'aggregates details' do
40
+ statuus = {
41
+ :one => ONE,
42
+ :two => TWO,
43
+ }
44
+ expect(subject.aggregate(statuus).to_h[:statusDetails]).to eq({
45
+ :one => {
46
+ :status => 'ERROR',
47
+ :message => 'foo',
48
+ },
49
+ :two => {
50
+ :status => 'WARNING',
51
+ :message => 'bar',
52
+ },
53
+ })
54
+ end
55
+ end
56
+
57
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-health-check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Bechstein
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-15 00:00:00.000000000 Z
11
+ date: 2015-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -120,13 +120,23 @@ files:
120
120
  - ".simplecov"
121
121
  - ".travis.yml"
122
122
  - Gemfile
123
+ - LICENSE
123
124
  - README.md
124
125
  - Rakefile
125
126
  - lib/sinatra-health-check.rb
126
127
  - lib/sinatra-health-check/checker.rb
128
+ - lib/sinatra-health-check/status.rb
129
+ - lib/sinatra-health-check/status/aggregated.rb
130
+ - lib/sinatra-health-check/status/forgiving_aggregator.rb
131
+ - lib/sinatra-health-check/status/overwriting_aggregator.rb
132
+ - lib/sinatra-health-check/status/strict_aggregator.rb
127
133
  - lib/sinatra-health-check/version.rb
128
134
  - sinatra-health-check.gemspec
129
135
  - spec/sinatra-health-check/checker_spec.rb
136
+ - spec/sinatra-health-check/forgiving_aggregator_spec.rb
137
+ - spec/sinatra-health-check/overwriting_aggregator_spec.rb
138
+ - spec/sinatra-health-check/status_spec.rb
139
+ - spec/sinatra-health-check/strict_aggregator_spec.rb
130
140
  - spec/spec_helper.rb
131
141
  homepage: https://github.com/otto-de/sinatra-health-check
132
142
  licenses:
@@ -154,4 +164,8 @@ specification_version: 4
154
164
  summary: This health check adds graceful stop to your sinatra applications
155
165
  test_files:
156
166
  - spec/sinatra-health-check/checker_spec.rb
167
+ - spec/sinatra-health-check/forgiving_aggregator_spec.rb
168
+ - spec/sinatra-health-check/overwriting_aggregator_spec.rb
169
+ - spec/sinatra-health-check/status_spec.rb
170
+ - spec/sinatra-health-check/strict_aggregator_spec.rb
157
171
  - spec/spec_helper.rb