sinatra-health-check 0.1.0 → 0.2.0

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: 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