on_strum-healthcheck 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +213 -0
  3. data/.circleci/gemspecs/compatible +25 -0
  4. data/.circleci/gemspecs/latest +33 -0
  5. data/.circleci/linter_configs/.bundler-audit.yml +4 -0
  6. data/.circleci/linter_configs/.commitspell.yml +30 -0
  7. data/.circleci/linter_configs/.cspell.yml +31 -0
  8. data/.circleci/linter_configs/.fasterer.yml +4 -0
  9. data/.circleci/linter_configs/.lefthook.yml +44 -0
  10. data/.circleci/linter_configs/.markdownlint.yml +9 -0
  11. data/.circleci/linter_configs/.rubocop.yml +137 -0
  12. data/.circleci/linter_configs/.yamllint.yml +7 -0
  13. data/.circleci/scripts/changeloglint.sh +22 -0
  14. data/.circleci/scripts/commitspell.sh +22 -0
  15. data/.circleci/scripts/release.sh +69 -0
  16. data/.circleci/scripts/set_publisher_credentials.sh +12 -0
  17. data/.codeclimate.yml +17 -0
  18. data/.github/BRANCH_NAMING_CONVENTION.md +36 -0
  19. data/.github/DEVELOPMENT_ENVIRONMENT_GUIDE.md +26 -0
  20. data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
  21. data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  22. data/.github/ISSUE_TEMPLATE/issue_report.md +32 -0
  23. data/.github/ISSUE_TEMPLATE/question.md +22 -0
  24. data/.github/PULL_REQUEST_TEMPLATE.md +49 -0
  25. data/.gitignore +11 -0
  26. data/.reek.yml +39 -0
  27. data/.rspec +2 -0
  28. data/.ruby-gemset +1 -0
  29. data/.ruby-version +1 -0
  30. data/CHANGELOG.md +9 -0
  31. data/CODE_OF_CONDUCT.md +74 -0
  32. data/CONTRIBUTING.md +48 -0
  33. data/Gemfile +5 -0
  34. data/LICENSE.txt +21 -0
  35. data/README.md +224 -0
  36. data/Rakefile +8 -0
  37. data/bin/console +15 -0
  38. data/bin/setup +8 -0
  39. data/lib/on_strum/healthcheck/configuration.rb +141 -0
  40. data/lib/on_strum/healthcheck/core.rb +22 -0
  41. data/lib/on_strum/healthcheck/error/configuration/argument_type.rb +15 -0
  42. data/lib/on_strum/healthcheck/error/configuration/enpoint_pattern.rb +15 -0
  43. data/lib/on_strum/healthcheck/error/configuration/http_status_failure.rb +15 -0
  44. data/lib/on_strum/healthcheck/error/configuration/http_status_success.rb +15 -0
  45. data/lib/on_strum/healthcheck/error/configuration/not_callable_service.rb +15 -0
  46. data/lib/on_strum/healthcheck/error/configuration/not_configured.rb +15 -0
  47. data/lib/on_strum/healthcheck/error/configuration/unknown_service.rb +15 -0
  48. data/lib/on_strum/healthcheck/rack_middleware.rb +28 -0
  49. data/lib/on_strum/healthcheck/resolver.rb +85 -0
  50. data/lib/on_strum/healthcheck/version.rb +7 -0
  51. data/lib/on_strum/healthcheck.rb +25 -0
  52. data/on_strum-healthcheck.gemspec +28 -0
  53. metadata +164 -0
data/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # ![OnStrum::Healthcheck - Simple configurable application healthcheck rack middleware](https://repository-images.githubusercontent.com/769335579/9094eefe-bfc8-483d-9a65-e406d1eb92c6)
2
+
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/b4dc21883d489d67fbef/maintainability)](https://codeclimate.com/github/on-strum/ruby-on-strum-healthcheck/maintainability)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/b4dc21883d489d67fbef/test_coverage)](https://codeclimate.com/github/on-strum/ruby-on-strum-healthcheck/test_coverage)
5
+ [![CircleCI](https://circleci.com/gh/on-strum/ruby-on-strum-healthcheck/tree/master.svg?style=svg)](https://circleci.com/gh/on-strum/ruby-on-strum-healthcheck/tree/master)
6
+ [![Gem Version](https://badge.fury.io/rb/ruby_healthcheck.svg)](https://badge.fury.io/rb/ruby_healthcheck)
7
+ [![Downloads](https://img.shields.io/gem/dt/ruby_healthcheck.svg?colorA=004d99&colorB=0073e6)](https://rubygems.org/gems/ruby_healthcheck)
8
+ [![GitHub](https://img.shields.io/github/license/on-strum/ruby-on-strum-healthcheck)](LICENSE.txt)
9
+ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)
10
+
11
+ Simple configurable application healthcheck rack middleware. This middleware allows you to embed healthcheck endpoints into your rack based application to perform healthcheck probes. Make your application compatible with [Docker](https://docs.docker.com/reference/dockerfile/#healthcheck)/[Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request) healthchecks in a seconds.
12
+
13
+ ## Table of Contents
14
+
15
+ - [Features](#features)
16
+ - [Requirements](#requirements)
17
+ - [Installation](#installation)
18
+ - [Configuring](#configuring)
19
+ - [Usage](#usage)
20
+ - [Rack](#rack)
21
+ - [Roda](#roda)
22
+ - [Hanami](#hanami)
23
+ - [Rails](#rails)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
26
+ - [Code of Conduct](#code-of-conduct)
27
+ - [Credits](#credits)
28
+ - [Versioning](#versioning)
29
+ - [Changelog](CHANGELOG.md)
30
+
31
+ ## Features
32
+
33
+ - Built-in default configuration
34
+ - Configurable services for startup/liveness/readiness probes
35
+ - Configurable root endpoints namespace
36
+ - Configurable startup/liveness/readiness probes endpoints
37
+ - Configurable successful/failure response statuses
38
+
39
+ ## Requirements
40
+
41
+ Ruby MRI 2.5.0+
42
+
43
+ ## Installation
44
+
45
+ Add this line to your application's Gemfile:
46
+
47
+ ```ruby
48
+ gem 'on_strum-healthcheck'
49
+ ```
50
+
51
+ And then execute:
52
+
53
+ ```bash
54
+ bundle
55
+ ```
56
+
57
+ Or install it yourself as:
58
+
59
+ ```bash
60
+ gem install on_strum-healthcheck
61
+ ```
62
+
63
+ ## Configuring
64
+
65
+ To start working with this gem, you must configure it first as in the example below:
66
+
67
+ ```ruby
68
+ # config/initializers/on_strum_healthcheck.rb
69
+
70
+ require 'on_strum/healthcheck'
71
+
72
+ OnStrum::Healthcheck.configure do |config|
73
+ # Optional parameter. The list of services that can be triggered
74
+ # during running probes. Each value of this hash should be callable
75
+ # and return boolean.
76
+ # It is equal to empty hash by default.
77
+ config.services = {
78
+ postges: -> { true },
79
+ redis: -> { true },
80
+ rabbit: -> { false }
81
+ }
82
+
83
+ # Optional parameter. The list of services that will be checked
84
+ # during running startup probe. As array items must be used an
85
+ # existing keys, defined in config.services.
86
+ # It is equal to empty array by default.
87
+ config.services_startup = %i[postges]
88
+
89
+ # Optional parameter. The list of services that will be checked
90
+ # during running liveness probe. As array items must be used an
91
+ # existing keys, defined in config.services.
92
+ # It is equal to empty array by default.
93
+ config.services_liveness = %i[redis]
94
+
95
+ # Optional parameter. The list of services that will be checked
96
+ # during running liveness probe. As array items must be used an
97
+ # existing keys, defined in config.services.
98
+ # It is equal to empty array by default.
99
+ config.services_readiness = %i[postges redis rabbit]
100
+
101
+ # Optional parameter. The name of middleware's root
102
+ # endpoints namespace. Use '/' if you want to use root
103
+ # namespace. It is equal to /healthcheck by default.
104
+ config.endpoints_namespace = '/application-healthcheck'
105
+
106
+ # Optional parameter. The startup endpoint path.
107
+ # It is equal to /startup by default.
108
+ config.endpoint_startup = '/startup-probe'
109
+
110
+ # Optional parameter. The liveness endpoint path.
111
+ # It is equal to /liveness by default.
112
+ config.endpoint_liveness = '/liveness-probe'
113
+
114
+ # Optional parameter. The readiness endpoint path.
115
+ # It is equal to /readiness by default.
116
+ config.endpoint_readiness = '/readiness-probe'
117
+
118
+ # Optional parameter. The HTTP successful status
119
+ # for startup probe. It is equal to 200 by default.
120
+ config.endpoint_startup_status_success = 201
121
+
122
+ # Optional parameter. The HTTP successful status
123
+ # for liveness probe. It is equal to 200 by default.
124
+ config.endpoint_liveness_status_success = 202
125
+
126
+ # Optional parameter. The HTTP successful status
127
+ # for readiness probe. It is equal to 200 by default.
128
+ config.endpoint_readiness_status_success = 203
129
+
130
+ # Optional parameter. The HTTP failure status
131
+ # for startup probe. It is equal to 500 by default.
132
+ config.endpoint_startup_status_failure = 501
133
+
134
+ # Optional parameter. The HTTP failure status
135
+ # for liveness probe. It is equal to 500 by default.
136
+ config.endpoint_liveness_status_failure = 502
137
+
138
+ # Optional parameter. The HTTP failure status
139
+ # for readiness probe. It is equal to 500 by default.
140
+ config.endpoint_readiness_status_failure = 503
141
+ end
142
+ ```
143
+
144
+ ## Usage
145
+
146
+ Please note, to start using this middleware you should configure `OnStrum::Healthcheck` before and then you should to add `OnStrum::Healthcheck::RackMiddleware` on the top of middlewares list.
147
+
148
+ ### Rack
149
+
150
+ ```ruby
151
+ require 'rack'
152
+ require 'on_strum/healthcheck'
153
+
154
+ OnStrum::Healthcheck.configure
155
+
156
+ RackCascade = Rack::Builder.app do
157
+ use OnStrum::Healthcheck::RackMiddleware
158
+ run YourApplication
159
+ end
160
+ ```
161
+
162
+ ### Roda
163
+
164
+ ```ruby
165
+ require 'roda'
166
+ require 'on_strum/healthcheck'
167
+
168
+ OnStrum::Healthcheck.configure
169
+
170
+ class YourApplication < Roda
171
+ use OnStrum::Healthcheck::RackMiddleware
172
+ end
173
+ ```
174
+
175
+ ### Hanami
176
+
177
+ ```ruby
178
+ # config/initializers/on_strum_healthcheck.rb
179
+
180
+ require 'on_strum/healthcheck'
181
+
182
+ OnStrum::Healthcheck.configure
183
+
184
+ # config/environment.rb
185
+
186
+ Hanami.configure do
187
+ middleware.use MyRackMiddleware
188
+ end
189
+ ```
190
+
191
+ ### Rails
192
+
193
+ ```ruby
194
+ # config/initializers/on_strum_healthcheck.rb
195
+
196
+ require 'on_strum/healthcheck'
197
+
198
+ OnStrum::Healthcheck.configure
199
+
200
+ # config/application.rb
201
+
202
+ config.middleware.use OnStrum::Healthcheck::RackMiddleware
203
+ ```
204
+
205
+ ## Contributing
206
+
207
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/on-strum/ruby-on-strum-healthcheck>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. Please check the [open tickets](https://github.com/on-strum/ruby-on-strum-healthcheck/issues). Be sure to follow Contributor Code of Conduct below and our [Contributing Guidelines](CONTRIBUTING.md).
208
+
209
+ ## License
210
+
211
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
212
+
213
+ ## Code of Conduct
214
+
215
+ Everyone interacting in the `on_strum-healthcheck` project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
216
+
217
+ ## Credits
218
+
219
+ - [The Contributors](https://github.com/on-strum/ruby-on-strum-healthcheck/graphs/contributors) for code and awesome suggestions
220
+ - [The Stargazers](https://github.com/on-strum/ruby-on-strum-healthcheck/stargazers) for showing their support
221
+
222
+ ## Versioning
223
+
224
+ `on_strum-healthcheck` uses [Semantic Versioning 2.0.0](https://semver.org)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'on_strum/healthcheck'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ class Configuration
6
+ ATTRIBUTES = %i[
7
+ services
8
+ services_startup
9
+ services_liveness
10
+ services_readiness
11
+ endpoints_namespace
12
+ endpoint_startup
13
+ endpoint_liveness
14
+ endpoint_readiness
15
+ endpoint_startup_status_success
16
+ endpoint_liveness_status_success
17
+ endpoint_readiness_status_success
18
+ endpoint_startup_status_failure
19
+ endpoint_liveness_status_failure
20
+ endpoint_readiness_status_failure
21
+ ].freeze
22
+ ENDPOINTS_NAMESPACE = '/healthcheck'
23
+ ENDPOINT_STARTUP = '/startup'
24
+ ENDPOINT_LIVENESS = '/liveness'
25
+ ENDPOINT_READINESS = '/readiness'
26
+ DEFAULT_HTTP_STATUS_SUCCESS = 200
27
+ DEFAULT_HTTP_STATUS_FAILURE = 500
28
+ AVILABLE_HTTP_STATUSES_SUCCESS = (DEFAULT_HTTP_STATUS_SUCCESS..226).freeze
29
+ AVILABLE_HTTP_STATUSES_FAILURE = (DEFAULT_HTTP_STATUS_FAILURE..511).freeze
30
+
31
+ Settings = ::Struct.new(*OnStrum::Healthcheck::Configuration::ATTRIBUTES, keyword_init: true) do
32
+ def update(&block)
33
+ return self unless block
34
+
35
+ tap(&block)
36
+ end
37
+ end
38
+
39
+ attr_reader(*OnStrum::Healthcheck::Configuration::ATTRIBUTES)
40
+
41
+ def initialize(&block)
42
+ configuration_settings = build_configuration_settings(&block)
43
+ OnStrum::Healthcheck::Configuration::ATTRIBUTES.each do |attribute|
44
+ public_send(:"#{attribute}=", configuration_settings.public_send(attribute))
45
+ end
46
+ end
47
+
48
+ OnStrum::Healthcheck::Configuration::ATTRIBUTES.each do |attribute|
49
+ define_method(:"#{attribute}=") do |argument|
50
+ validate_attribute(__method__, attribute, argument)
51
+ instance_variable_set(:"@#{attribute}", argument)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def build_configuration_settings(&block) # rubocop:disable Metrics/MethodLength
58
+ OnStrum::Healthcheck::Configuration::Settings.new(
59
+ services: {},
60
+ services_startup: [],
61
+ services_liveness: [],
62
+ services_readiness: [],
63
+ endpoints_namespace: OnStrum::Healthcheck::Configuration::ENDPOINTS_NAMESPACE,
64
+ endpoint_startup: OnStrum::Healthcheck::Configuration::ENDPOINT_STARTUP,
65
+ endpoint_liveness: OnStrum::Healthcheck::Configuration::ENDPOINT_LIVENESS,
66
+ endpoint_readiness: OnStrum::Healthcheck::Configuration::ENDPOINT_READINESS,
67
+ endpoint_startup_status_success: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
68
+ endpoint_liveness_status_success: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
69
+ endpoint_readiness_status_success: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
70
+ endpoint_startup_status_failure: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_FAILURE,
71
+ endpoint_liveness_status_failure: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_FAILURE,
72
+ endpoint_readiness_status_failure: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_FAILURE
73
+ ).update(&block)
74
+ end
75
+
76
+ def validate_attribute(method, attribute, value) # rubocop:disable Metrics/AbcSize
77
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::ArgumentType, method, *validator_argument_type(attribute, value))
78
+ case attribute
79
+ when OnStrum::Healthcheck::Configuration::ATTRIBUTES[0]
80
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::NotCallableService, method, *validator_services_callable(value))
81
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[1..3]
82
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::UnknownService, method, *validator_services_conformity(value))
83
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[4..7]
84
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::EnpointPattern, method, *validator_endpoint(value))
85
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[8..10]
86
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::HttpStatusSuccess, method, *validator_http_status_success(value))
87
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[11..13]
88
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::HttpStatusFailure, method, *validator_http_status_failure(value))
89
+ end
90
+ end
91
+
92
+ def validator_argument_type(method_name, argument)
93
+ [
94
+ argument,
95
+ argument.is_a?(
96
+ case method_name
97
+ when :services then ::Hash
98
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[1..3] then ::Array
99
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[4..7] then ::String
100
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[8..13] then ::Integer
101
+ end
102
+ )
103
+ ]
104
+ end
105
+
106
+ def validator_endpoint(argument)
107
+ [argument, argument[%r{\A/.*\z}]]
108
+ end
109
+
110
+ def validator_http_status_success(argument)
111
+ [argument, OnStrum::Healthcheck::Configuration::AVILABLE_HTTP_STATUSES_SUCCESS.include?(argument)]
112
+ end
113
+
114
+ def validator_http_status_failure(argument)
115
+ [argument, OnStrum::Healthcheck::Configuration::AVILABLE_HTTP_STATUSES_FAILURE.include?(argument)]
116
+ end
117
+
118
+ def validator_services_callable(services_to_check, target_service = nil)
119
+ result = services_to_check.all? do |service_name, service_context|
120
+ target_service = service_name
121
+ service_context.respond_to?(:call)
122
+ end
123
+
124
+ [target_service, result]
125
+ end
126
+
127
+ def validator_services_conformity(services_to_check, target_service = nil)
128
+ result = services_to_check.all? do |service_name|
129
+ target_service = service_name
130
+ services.key?(service_name)
131
+ end
132
+
133
+ [target_service, result]
134
+ end
135
+
136
+ def raise_unless(exception_class, argument_name, argument_context, condition)
137
+ raise exception_class.new(argument_context, argument_name) unless condition
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ require_relative 'error/configuration/argument_type'
8
+ require_relative 'error/configuration/unknown_service'
9
+ require_relative 'error/configuration/not_callable_service'
10
+ require_relative 'error/configuration/enpoint_pattern'
11
+ require_relative 'error/configuration/http_status_success'
12
+ require_relative 'error/configuration/http_status_failure'
13
+ require_relative 'error/configuration/not_configured'
14
+ end
15
+ end
16
+
17
+ require_relative 'version'
18
+ require_relative 'configuration'
19
+ require_relative 'resolver'
20
+ require_relative 'rack_middleware'
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ ArgumentType = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("#{arg_value} is not a valid #{arg_name}")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ EnpointPattern = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("#{arg_value} does not match a valid enpoint pattern for #{arg_name}")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ HttpStatusFailure = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("Status #{arg_value} is wrong HTTP failure status for #{arg_name}. It should be in the range 500-511")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ HttpStatusSuccess = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("Status #{arg_value} is wrong HTTP successful status for #{arg_name}. It should be in the range 200-226")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ NotCallableService = ::Class.new(::ArgumentError) do
8
+ def initialize(service_name, services_setter)
9
+ super("Service #{service_name} is not callable. All values for #{services_setter} should be a callable objects")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ NotConfigured = ::Class.new(::RuntimeError) do
8
+ def initialize
9
+ super('The configuration is empty. Please use OnStrum::Healthcheck.configure before')
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ UnknownService = ::Class.new(::ArgumentError) do
8
+ def initialize(service_name, services_setter)
9
+ super("Unknown #{service_name} service name for #{services_setter}. You should define it in config.services firstly")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ class RackMiddleware
6
+ def initialize(
7
+ app,
8
+ resolver = OnStrum::Healthcheck::Resolver,
9
+ counfigured = !!OnStrum::Healthcheck.configuration,
10
+ *
11
+ )
12
+ @app = app
13
+ @resolver = resolver
14
+ @counfigured = counfigured
15
+ end
16
+
17
+ def call(env)
18
+ raise OnStrum::Healthcheck::Error::Configuration::NotConfigured unless counfigured
19
+
20
+ resolver.call(env) || app.call(env)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :app, :resolver, :counfigured
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ class Resolver
6
+ require 'rack'
7
+ require 'securerandom'
8
+ require 'json'
9
+
10
+ PROBE_ENDPOINTS = %i[endpoint_startup endpoint_liveness endpoint_readiness].freeze
11
+ CONTENT_TYPE = { 'Content-Type' => 'application/json' }.freeze
12
+ JSONAPI_RESPONSE_TYPE = 'application-healthcheck'
13
+ ROOT_NAMESPACE = '/'
14
+
15
+ def self.call(rack_env)
16
+ new(rack_env).call
17
+ end
18
+
19
+ def initialize(rack_env)
20
+ @request = ::Rack::Request.new(rack_env)
21
+ end
22
+
23
+ def call
24
+ return unless probe_name
25
+
26
+ [response_status, OnStrum::Healthcheck::Resolver::CONTENT_TYPE, [response_jsonapi]]
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :request
32
+
33
+ def configuration
34
+ OnStrum::Healthcheck.configuration
35
+ end
36
+
37
+ def root_namespace
38
+ @root_namespace ||= configuration.endpoints_namespace
39
+ end
40
+
41
+ def root_namespace?
42
+ root_namespace.eql?(OnStrum::Healthcheck::Resolver::ROOT_NAMESPACE)
43
+ end
44
+
45
+ OnStrum::Healthcheck::Resolver::PROBE_ENDPOINTS.each do |method|
46
+ define_method(method) do
47
+ target_endpoint = configuration.public_send(method)
48
+ root_namespace? ? target_endpoint : "#{root_namespace}#{target_endpoint}"
49
+ end
50
+ end
51
+
52
+ def probe_name
53
+ @probe_name ||=
54
+ case request.path
55
+ when endpoint_startup then :startup
56
+ when endpoint_liveness then :liveness
57
+ when endpoint_readiness then :readiness
58
+ end
59
+ end
60
+
61
+ def probe_result
62
+ @probe_result ||=
63
+ configuration.public_send(:"services_#{probe_name}").each_with_object({}) do |service_name, response|
64
+ response[service_name] = configuration.services[service_name].call
65
+ end
66
+ end
67
+
68
+ def response_status
69
+ configuration.public_send(
70
+ probe_result.values.all? ? :"endpoint_#{probe_name}_status_success" : :"endpoint_#{probe_name}_status_failure"
71
+ )
72
+ end
73
+
74
+ def response_jsonapi
75
+ {
76
+ data: {
77
+ id: ::SecureRandom.uuid,
78
+ type: OnStrum::Healthcheck::Resolver::JSONAPI_RESPONSE_TYPE,
79
+ attributes: probe_result
80
+ }
81
+ }.to_json
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'healthcheck/core'
4
+
5
+ module OnStrum
6
+ module Healthcheck
7
+ class << self
8
+ def configuration(&block)
9
+ @configuration ||= begin
10
+ return unless block
11
+
12
+ OnStrum::Healthcheck::Configuration.new(&block)
13
+ end
14
+ end
15
+
16
+ def configure(&block)
17
+ configuration(&block)
18
+ end
19
+
20
+ def reset_configuration!
21
+ @configuration = nil
22
+ end
23
+ end
24
+ end
25
+ end